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_region.h"
34 #include "ardour/midi_source.h"
35 #include "ardour/midi_model.h"
36 #include "ardour/midi_patch_manager.h"
37 #include "ardour/session.h"
39 #include "evoral/Parameter.hpp"
40 #include "evoral/MIDIParameters.hpp"
41 #include "evoral/MIDIEvent.hpp"
42 #include "evoral/Control.hpp"
43 #include "evoral/midi_util.h"
45 #include "automation_region_view.h"
46 #include "automation_time_axis.h"
47 #include "canvas-hit.h"
48 #include "canvas-note.h"
49 #include "canvas_patch_change.h"
50 #include "canvas-sysex.h"
53 #include "editor_drag.h"
54 #include "ghostregion.h"
55 #include "gui_thread.h"
57 #include "midi_channel_dialog.h"
58 #include "midi_cut_buffer.h"
59 #include "midi_list_editor.h"
60 #include "midi_region_view.h"
61 #include "midi_streamview.h"
62 #include "midi_time_axis.h"
63 #include "midi_util.h"
64 #include "midi_velocity_dialog.h"
65 #include "mouse_cursors.h"
66 #include "note_player.h"
67 #include "public_editor.h"
68 #include "rgb_macros.h"
69 #include "selection.h"
70 #include "simpleline.h"
71 #include "streamview.h"
73 #include "patch_change_dialog.h"
74 #include "verbose_cursor.h"
78 using namespace ARDOUR;
80 using namespace Editing;
81 using namespace ArdourCanvas;
82 using Gtkmm2ext::Keyboard;
84 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
86 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
88 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
89 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
90 : RegionView (parent, tv, r, spu, basic_color)
91 , _last_channel_selection(0xFFFF)
92 , _current_range_min(0)
93 , _current_range_max(0)
95 , _note_group(new ArdourCanvas::Group(*group))
96 , _note_diff_command (0)
98 , _step_edit_cursor (0)
99 , _step_edit_cursor_width (1.0)
100 , _step_edit_cursor_position (0.0)
101 , _channel_selection_scoped_note (0)
102 , _temporary_note_group (0)
105 , _sort_needed (true)
106 , _optimization_iterator (_events.end())
108 , _no_sound_notes (false)
111 , pre_enter_cursor (0)
112 , pre_press_cursor (0)
115 _note_group->raise_to_top();
116 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
119 MidiTimeAxisView *time_axis = dynamic_cast<MidiTimeAxisView *>(&tv);
121 _last_channel_mode = time_axis->channel_selector().get_channel_mode();
122 _last_channel_selection = time_axis->channel_selector().get_selected_channels();
125 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
126 connect_to_diskstream ();
128 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
131 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
132 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
133 TimeAxisViewItem::Visibility visibility)
134 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
135 , _last_channel_selection(0xFFFF)
136 , _current_range_min(0)
137 , _current_range_max(0)
139 , _note_group(new ArdourCanvas::Group(*parent))
140 , _note_diff_command (0)
142 , _step_edit_cursor (0)
143 , _step_edit_cursor_width (1.0)
144 , _step_edit_cursor_position (0.0)
145 , _channel_selection_scoped_note (0)
146 , _temporary_note_group (0)
149 , _sort_needed (true)
150 , _optimization_iterator (_events.end())
152 , _no_sound_notes (false)
155 , pre_enter_cursor (0)
156 , pre_press_cursor (0)
159 _note_group->raise_to_top();
160 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
162 MidiTimeAxisView *time_axis = dynamic_cast<MidiTimeAxisView *>(&tv);
164 _last_channel_mode = time_axis->channel_selector().get_channel_mode();
165 _last_channel_selection = time_axis->channel_selector().get_selected_channels();
168 connect_to_diskstream ();
170 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
174 MidiRegionView::parameter_changed (std::string const & p)
176 if (p == "diplay-first-midi-bank-as-zero") {
177 if (_enable_display) {
183 MidiRegionView::MidiRegionView (const MidiRegionView& other)
184 : sigc::trackable(other)
186 , _last_channel_selection(0xFFFF)
187 , _current_range_min(0)
188 , _current_range_max(0)
190 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
191 , _note_diff_command (0)
193 , _step_edit_cursor (0)
194 , _step_edit_cursor_width (1.0)
195 , _step_edit_cursor_position (0.0)
196 , _channel_selection_scoped_note (0)
197 , _temporary_note_group (0)
200 , _sort_needed (true)
201 , _optimization_iterator (_events.end())
203 , _no_sound_notes (false)
206 , pre_enter_cursor (0)
207 , pre_press_cursor (0)
213 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
214 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
219 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
220 : RegionView (other, boost::shared_ptr<Region> (region))
221 , _last_channel_selection(0xFFFF)
222 , _current_range_min(0)
223 , _current_range_max(0)
225 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
226 , _note_diff_command (0)
228 , _step_edit_cursor (0)
229 , _step_edit_cursor_width (1.0)
230 , _step_edit_cursor_position (0.0)
231 , _channel_selection_scoped_note (0)
232 , _temporary_note_group (0)
235 , _sort_needed (true)
236 , _optimization_iterator (_events.end())
238 , _no_sound_notes (false)
241 , pre_enter_cursor (0)
242 , pre_press_cursor (0)
248 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
249 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
255 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
257 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
259 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
260 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
264 midi_region()->midi_source(0)->load_model();
267 _model = midi_region()->midi_source(0)->model();
268 _enable_display = false;
270 RegionView::init (basic_color, false);
272 compute_colors (basic_color);
274 set_height (trackview.current_height());
277 region_sync_changed ();
278 region_resized (ARDOUR::bounds_change);
283 _enable_display = true;
286 display_model (_model);
290 reset_width_dependent_items (_pixel_width);
292 group->raise_to_top();
293 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
295 midi_view()->signal_channel_mode_changed().connect(
296 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
298 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
299 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
301 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
302 boost::bind (&MidiRegionView::snap_changed, this),
305 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
306 connect_to_diskstream ();
308 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
312 MidiRegionView::instrument_info () const
314 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
315 return route_ui->route()->instrument_info();
318 const boost::shared_ptr<ARDOUR::MidiRegion>
319 MidiRegionView::midi_region() const
321 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
325 MidiRegionView::connect_to_diskstream ()
327 midi_view()->midi_track()->DataRecorded.connect(
328 *this, invalidator(*this),
329 boost::bind (&MidiRegionView::data_recorded, this, _1),
334 MidiRegionView::canvas_event(GdkEvent* ev)
339 case GDK_ENTER_NOTIFY:
340 case GDK_LEAVE_NOTIFY:
341 _last_event_x = ev->crossing.x;
342 _last_event_y = ev->crossing.y;
344 case GDK_MOTION_NOTIFY:
345 _last_event_x = ev->motion.x;
346 _last_event_y = ev->motion.y;
352 if (ev->type == GDK_2BUTTON_PRESS) {
353 // cannot use double-click to exit internal mode if single-click is being used
354 MouseMode m = trackview.editor().current_mouse_mode();
356 if ((m != MouseObject || !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier())) && (m != MouseDraw)) {
357 return trackview.editor().toggle_internal_editing_from_double_click (ev);
361 if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) ||
362 (trackview.editor().current_mouse_mode() == MouseTimeFX) ||
363 (trackview.editor().current_mouse_mode() == MouseZoom)) {
364 // handle non-draw modes elsewhere
370 return scroll (&ev->scroll);
373 return key_press (&ev->key);
375 case GDK_KEY_RELEASE:
376 return key_release (&ev->key);
378 case GDK_BUTTON_PRESS:
379 return button_press (&ev->button);
381 case GDK_BUTTON_RELEASE:
382 r = button_release (&ev->button);
387 case GDK_ENTER_NOTIFY:
388 return enter_notify (&ev->crossing);
390 case GDK_LEAVE_NOTIFY:
391 return leave_notify (&ev->crossing);
393 case GDK_MOTION_NOTIFY:
394 return motion (&ev->motion);
404 MidiRegionView::remove_ghost_note ()
411 MidiRegionView::enter_notify (GdkEventCrossing* ev)
413 trackview.editor().MouseModeChanged.connect (
414 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
417 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
418 create_ghost_note (ev->x, ev->y);
421 if (!trackview.editor().internal_editing()) {
422 Keyboard::magic_widget_drop_focus();
424 Keyboard::magic_widget_grab_focus();
428 // if current operation is non-operational in a midi region, change the cursor to so indicate
429 if (trackview.editor().current_mouse_mode() == MouseGain) {
430 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
431 pre_enter_cursor = editor->get_canvas_cursor();
432 editor->set_canvas_cursor(editor->cursors()->timebar);
439 MidiRegionView::leave_notify (GdkEventCrossing*)
441 _mouse_mode_connection.disconnect ();
443 trackview.editor().verbose_cursor()->hide ();
444 remove_ghost_note ();
446 if (pre_enter_cursor) {
447 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
448 editor->set_canvas_cursor(pre_enter_cursor);
455 MidiRegionView::mouse_mode_changed ()
457 if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) {
458 create_ghost_note (_last_event_x, _last_event_y);
460 remove_ghost_note ();
461 trackview.editor().verbose_cursor()->hide ();
464 if (!trackview.editor().internal_editing()) {
465 Keyboard::magic_widget_drop_focus();
467 Keyboard::magic_widget_grab_focus();
473 MidiRegionView::button_press (GdkEventButton* ev)
475 if (ev->button != 1) {
479 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
480 MouseMode m = editor->current_mouse_mode();
482 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
483 pre_press_cursor = editor->get_canvas_cursor ();
484 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
487 if (_mouse_state != SelectTouchDragging) {
489 _pressed_button = ev->button;
490 _mouse_state = Pressed;
495 _pressed_button = ev->button;
501 MidiRegionView::button_release (GdkEventButton* ev)
503 double event_x, event_y;
505 if (ev->button != 1) {
512 group->w2i(event_x, event_y);
513 group->ungrab(ev->time);
515 PublicEditor& editor = trackview.editor ();
517 if (pre_press_cursor) {
518 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
519 pre_press_cursor = 0;
522 switch (_mouse_state) {
523 case Pressed: // Clicked
525 switch (editor.current_mouse_mode()) {
527 /* no motion occured - simple click */
536 if (Keyboard::is_insert_note_event(ev)) {
538 double event_x, event_y;
542 group->w2i(event_x, event_y);
545 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
551 /* Shorten the length by 1 tick so that we can add a new note at the next
552 grid snap without it overlapping this one.
554 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
556 create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true);
564 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
570 /* Shorten the length by 1 tick so that we can add a new note at the next
571 grid snap without it overlapping this one.
573 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
575 create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true);
586 case SelectRectDragging:
588 editor.drags()->end_grab ((GdkEvent *) ev);
590 create_ghost_note (ev->x, ev->y);
602 MidiRegionView::motion (GdkEventMotion* ev)
604 PublicEditor& editor = trackview.editor ();
606 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
607 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
608 _mouse_state != AddDragging) {
610 create_ghost_note (ev->x, ev->y);
612 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
613 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
615 update_ghost_note (ev->x, ev->y);
617 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
619 remove_ghost_note ();
620 editor.verbose_cursor()->hide ();
622 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
624 update_ghost_note (ev->x, ev->y);
627 /* any motion immediately hides velocity text that may have been visible */
629 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
630 (*i)->hide_velocity ();
633 switch (_mouse_state) {
636 if (_pressed_button == 1) {
638 MouseMode m = editor.current_mouse_mode();
640 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
642 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
643 _mouse_state = AddDragging;
644 remove_ghost_note ();
645 editor.verbose_cursor()->hide ();
647 } else if (m == MouseObject) {
648 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
650 _mouse_state = SelectRectDragging;
652 } else if (m == MouseRange) {
653 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
654 _mouse_state = SelectVerticalDragging;
661 case SelectRectDragging:
662 case SelectVerticalDragging:
664 editor.drags()->motion_handler ((GdkEvent *) ev, false);
667 case SelectTouchDragging:
679 MidiRegionView::scroll (GdkEventScroll* ev)
681 if (_selection.empty()) {
685 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
686 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
687 it still works for zoom.
692 trackview.editor().verbose_cursor()->hide ();
694 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
695 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
697 if (ev->direction == GDK_SCROLL_UP) {
698 change_velocities (true, fine, false, together);
699 } else if (ev->direction == GDK_SCROLL_DOWN) {
700 change_velocities (false, fine, false, together);
702 /* left, right: we don't use them */
710 MidiRegionView::key_press (GdkEventKey* ev)
712 /* since GTK bindings are generally activated on press, and since
713 detectable auto-repeat is the name of the game and only sends
714 repeated presses, carry out key actions at key press, not release.
717 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
719 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
720 _mouse_state = SelectTouchDragging;
723 } else if (ev->keyval == GDK_Escape && unmodified) {
727 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
729 bool start = (ev->keyval == GDK_comma);
730 bool end = (ev->keyval == GDK_period);
731 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
732 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
734 change_note_lengths (fine, shorter, 0.0, start, end);
738 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
740 if (_selection.empty()) {
747 } else if (ev->keyval == GDK_Tab) {
749 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
750 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
752 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
756 } else if (ev->keyval == GDK_ISO_Left_Tab) {
758 /* Shift-TAB generates ISO Left Tab, for some reason */
760 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
761 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
763 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
769 } else if (ev->keyval == GDK_Up) {
771 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
772 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
773 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
775 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
776 change_velocities (true, fine, allow_smush, together);
778 transpose (true, fine, allow_smush);
782 } else if (ev->keyval == GDK_Down) {
784 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
785 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
786 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
788 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
789 change_velocities (false, fine, allow_smush, together);
791 transpose (false, fine, allow_smush);
795 } else if (ev->keyval == GDK_Left && unmodified) {
800 } else if (ev->keyval == GDK_Right && unmodified) {
805 } else if (ev->keyval == GDK_c && unmodified) {
809 } else if (ev->keyval == GDK_v && unmodified) {
818 MidiRegionView::key_release (GdkEventKey* ev)
820 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
828 MidiRegionView::channel_edit ()
830 if (_selection.empty()) {
834 /* pick a note somewhat at random (since Selection is a set<>) to
835 * provide the "current" channel for the dialog.
838 uint8_t current_channel = (*_selection.begin())->note()->channel ();
839 MidiChannelDialog channel_dialog (current_channel);
840 int ret = channel_dialog.run ();
843 case Gtk::RESPONSE_OK:
849 uint8_t new_channel = channel_dialog.active_channel ();
851 start_note_diff_command (_("channel edit"));
853 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
854 Selection::iterator next = i;
856 change_note_channel (*i, new_channel);
864 MidiRegionView::velocity_edit ()
866 if (_selection.empty()) {
870 /* pick a note somewhat at random (since Selection is a set<>) to
871 * provide the "current" velocity for the dialog.
874 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
875 MidiVelocityDialog velocity_dialog (current_velocity);
876 int ret = velocity_dialog.run ();
879 case Gtk::RESPONSE_OK:
885 uint8_t new_velocity = velocity_dialog.velocity ();
887 start_note_diff_command (_("velocity edit"));
889 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
890 Selection::iterator next = i;
892 change_note_velocity (*i, new_velocity, false);
900 MidiRegionView::show_list_editor ()
903 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
905 _list_editor->present ();
908 /** Add a note to the model, and the view, at a canvas (click) coordinate.
909 * \param t time in frames relative to the position of the region
910 * \param y vertical position in pixels
911 * \param length duration of the note in beats
912 * \param snap_t true to snap t to the grid, otherwise false.
915 MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t)
917 if (length < 2 * DBL_EPSILON) {
921 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
922 MidiStreamView* const view = mtv->midi_view();
924 const double note = view->y_to_note(y);
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);
932 const boost::shared_ptr<NoteType> new_note (
933 new NoteType (mtv->get_channel_for_add (),
934 region_frames_to_region_beats(t + _region->start()),
936 (uint8_t)note, 0x40));
938 if (_model->contains (new_note)) {
942 view->update_note_range(new_note->note());
944 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
946 _model->apply_command(*trackview.session(), cmd);
948 play_midi_note (new_note);
952 MidiRegionView::clear_events (bool with_selection_signal)
954 clear_selection (with_selection_signal);
957 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
958 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
963 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
968 _patch_changes.clear();
970 _optimization_iterator = _events.end();
974 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
978 content_connection.disconnect ();
979 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
983 if (_enable_display) {
989 MidiRegionView::start_note_diff_command (string name)
991 if (!_note_diff_command) {
992 _note_diff_command = _model->new_note_diff_command (name);
997 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
999 if (_note_diff_command) {
1000 _note_diff_command->add (note);
1003 _marked_for_selection.insert(note);
1005 if (show_velocity) {
1006 _marked_for_velocity.insert(note);
1011 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
1013 if (_note_diff_command && ev->note()) {
1014 _note_diff_command->remove(ev->note());
1019 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1020 MidiModel::NoteDiffCommand::Property property,
1023 if (_note_diff_command) {
1024 _note_diff_command->change (ev->note(), property, val);
1029 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1030 MidiModel::NoteDiffCommand::Property property,
1031 Evoral::MusicalTime val)
1033 if (_note_diff_command) {
1034 _note_diff_command->change (ev->note(), property, val);
1039 MidiRegionView::apply_diff (bool as_subcommand)
1043 if (!_note_diff_command) {
1047 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1048 // Mark all selected notes for selection when model reloads
1049 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1050 _marked_for_selection.insert((*i)->note());
1054 if (as_subcommand) {
1055 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1057 _model->apply_command (*trackview.session(), _note_diff_command);
1060 _note_diff_command = 0;
1061 midi_view()->midi_track()->playlist_modified();
1063 if (add_or_remove) {
1064 _marked_for_selection.clear();
1067 _marked_for_velocity.clear();
1071 MidiRegionView::abort_command()
1073 delete _note_diff_command;
1074 _note_diff_command = 0;
1079 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1081 if (_optimization_iterator != _events.end()) {
1082 ++_optimization_iterator;
1085 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1086 return *_optimization_iterator;
1089 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1090 if ((*_optimization_iterator)->note() == note) {
1091 return *_optimization_iterator;
1099 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1101 MidiModel::Notes notes;
1102 _model->get_notes (notes, op, val, chan_mask);
1104 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1105 CanvasNoteEvent* cne = find_canvas_note (*n);
1113 MidiRegionView::redisplay_model()
1115 // Don't redisplay the model if we're currently recording and displaying that
1116 if (_active_notes) {
1124 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1125 (*i)->invalidate ();
1128 MidiModel::ReadLock lock(_model->read_lock());
1130 MidiModel::Notes& notes (_model->notes());
1131 _optimization_iterator = _events.begin();
1133 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1135 boost::shared_ptr<NoteType> note (*n);
1136 CanvasNoteEvent* cne;
1139 if (note_in_region_range (note, visible)) {
1141 if ((cne = find_canvas_note (note)) != 0) {
1148 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1150 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1162 add_note (note, visible);
1167 if ((cne = find_canvas_note (note)) != 0) {
1175 /* remove note items that are no longer valid */
1177 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1178 if (!(*i)->valid ()) {
1180 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1181 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1183 gr->remove_note (*i);
1188 i = _events.erase (i);
1195 _patch_changes.clear();
1199 display_patch_changes ();
1201 _marked_for_selection.clear ();
1202 _marked_for_velocity.clear ();
1204 /* we may have caused _events to contain things out of order (e.g. if a note
1205 moved earlier or later). we don't generally need them in time order, but
1206 make a note that a sort is required for those cases that require it.
1209 _sort_needed = true;
1213 MidiRegionView::display_patch_changes ()
1215 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1216 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1218 for (uint8_t i = 0; i < 16; ++i) {
1219 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1223 /** @param active_channel true to display patch changes fully, false to display
1224 * them `greyed-out' (as on an inactive channel)
1227 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1229 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1231 if ((*i)->channel() != channel) {
1235 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1236 add_canvas_patch_change (*i, patch_name, active_channel);
1241 MidiRegionView::display_sysexes()
1243 bool have_periodic_system_messages = false;
1244 bool display_periodic_messages = true;
1246 if (!Config->get_never_display_periodic_midi()) {
1248 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1249 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1250 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1253 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1254 have_periodic_system_messages = true;
1260 if (have_periodic_system_messages) {
1261 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1263 /* get an approximate value for the number of samples per video frame */
1265 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1267 /* if we are zoomed out beyond than the cutoff (i.e. more
1268 * frames per pixel than frames per 4 video frames), don't
1269 * show periodic sysex messages.
1272 if (zoom > (video_frame*4)) {
1273 display_periodic_messages = false;
1277 display_periodic_messages = false;
1280 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1282 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1283 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1285 Evoral::MusicalTime time = (*i)->time();
1288 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1289 if (!display_periodic_messages) {
1297 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1298 str << int((*i)->buffer()[b]);
1299 if (b != (*i)->size() -1) {
1303 string text = str.str();
1305 const double x = trackview.editor().frame_to_pixel(source_beats_to_region_frames(time));
1307 double height = midi_stream_view()->contents_height();
1309 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1310 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0, (*i)));
1312 // Show unless message is beyond the region bounds
1313 if (time - _region->start() >= _region->length() || time < _region->start()) {
1319 _sys_exes.push_back(sysex);
1323 MidiRegionView::~MidiRegionView ()
1325 in_destructor = true;
1327 trackview.editor().verbose_cursor()->hide ();
1329 note_delete_connection.disconnect ();
1331 delete _list_editor;
1333 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1335 if (_active_notes) {
1339 _selection_cleared_connection.disconnect ();
1342 clear_events (false);
1345 delete _note_diff_command;
1346 delete _step_edit_cursor;
1347 delete _temporary_note_group;
1351 MidiRegionView::region_resized (const PropertyChange& what_changed)
1353 RegionView::region_resized(what_changed);
1355 if (what_changed.contains (ARDOUR::Properties::position)) {
1356 set_duration(_region->length(), 0);
1357 if (_enable_display) {
1364 MidiRegionView::reset_width_dependent_items (double pixel_width)
1366 RegionView::reset_width_dependent_items(pixel_width);
1368 if (_enable_display) {
1372 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1373 if ((*x)->width() >= _pixel_width) {
1380 move_step_edit_cursor (_step_edit_cursor_position);
1381 set_step_edit_cursor_width (_step_edit_cursor_width);
1385 MidiRegionView::set_height (double height)
1387 static const double FUDGE = 2.0;
1388 const double old_height = _height;
1389 RegionView::set_height(height);
1390 _height = height - FUDGE;
1392 apply_note_range(midi_stream_view()->lowest_note(),
1393 midi_stream_view()->highest_note(),
1394 height != old_height + FUDGE);
1397 name_pixbuf->raise_to_top();
1400 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1401 (*x)->set_height (midi_stream_view()->contents_height());
1404 if (_step_edit_cursor) {
1405 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1410 /** Apply the current note range from the stream view
1411 * by repositioning/hiding notes as necessary
1414 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1416 if (!_enable_display) {
1420 if (!force && _current_range_min == min && _current_range_max == max) {
1424 _current_range_min = min;
1425 _current_range_max = max;
1427 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1428 CanvasNoteEvent* event = *i;
1429 boost::shared_ptr<NoteType> note (event->note());
1431 if (note->note() < _current_range_min ||
1432 note->note() > _current_range_max) {
1438 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1440 const double y1 = midi_stream_view()->note_to_y(note->note());
1441 const double y2 = y1 + floor(midi_stream_view()->note_height());
1443 cnote->property_y1() = y1;
1444 cnote->property_y2() = y2;
1446 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1448 const double diamond_size = update_hit (chit);
1450 chit->set_height (diamond_size);
1456 MidiRegionView::add_ghost (TimeAxisView& tv)
1460 double unit_position = _region->position () / samples_per_unit;
1461 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1462 MidiGhostRegion* ghost;
1464 if (mtv && mtv->midi_view()) {
1465 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1466 to allow having midi notes on top of note lines and waveforms.
1468 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1470 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1473 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1474 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1475 ghost->add_note(note);
1479 ghost->set_height ();
1480 ghost->set_duration (_region->length() / samples_per_unit);
1481 ghosts.push_back (ghost);
1483 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1489 /** Begin tracking note state for successive calls to add_event
1492 MidiRegionView::begin_write()
1494 if (_active_notes) {
1495 delete[] _active_notes;
1497 _active_notes = new CanvasNote*[128];
1498 for (unsigned i = 0; i < 128; ++i) {
1499 _active_notes[i] = 0;
1504 /** Destroy note state for add_event
1507 MidiRegionView::end_write()
1509 delete[] _active_notes;
1511 _marked_for_selection.clear();
1512 _marked_for_velocity.clear();
1516 /** Resolve an active MIDI note (while recording).
1519 MidiRegionView::resolve_note(uint8_t note, double end_time)
1521 if (midi_view()->note_mode() != Sustained) {
1525 if (_active_notes && _active_notes[note]) {
1527 /* XXX is end_time really region-centric? I think so, because
1528 this is a new region that we're recording, so source zero is
1529 the same as region zero
1531 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1533 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1534 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1535 _active_notes[note] = 0;
1540 /** Extend active notes to rightmost edge of region (if length is changed)
1543 MidiRegionView::extend_active_notes()
1545 if (!_active_notes) {
1549 for (unsigned i=0; i < 128; ++i) {
1550 if (_active_notes[i]) {
1551 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1558 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1560 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1564 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1566 if (!route_ui || !route_ui->midi_track()) {
1570 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1574 /* NotePlayer deletes itself */
1578 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1580 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1584 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1586 if (!route_ui || !route_ui->midi_track()) {
1590 delete _note_player;
1591 _note_player = new NotePlayer (route_ui->midi_track ());
1592 _note_player->add (note);
1593 _note_player->on ();
1597 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1599 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1603 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1605 if (!route_ui || !route_ui->midi_track()) {
1609 delete _note_player;
1610 _note_player = new NotePlayer (route_ui->midi_track());
1612 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1613 _note_player->add (*n);
1616 _note_player->on ();
1621 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1623 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1624 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1626 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1627 (note->note() <= midi_stream_view()->highest_note());
1632 /** Update a canvas note's size from its model note.
1633 * @param ev Canvas note to update.
1634 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1637 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1639 boost::shared_ptr<NoteType> note = ev->note();
1640 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time()));
1641 const double y1 = midi_stream_view()->note_to_y(note->note());
1643 ev->property_x1() = x;
1644 ev->property_y1() = y1;
1646 /* trim note display to not overlap the end of its region */
1648 if (note->length() > 0) {
1649 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1650 ev->property_x2() = trackview.editor().frame_to_pixel (note_end_frames);
1652 ev->property_x2() = trackview.editor().frame_to_pixel (_region->length());
1655 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1657 if (note->length() == 0) {
1658 if (_active_notes && note->note() < 128) {
1659 // If this note is already active there's a stuck note,
1660 // finish the old note rectangle
1661 if (_active_notes[note->note()]) {
1662 CanvasNote* const old_rect = _active_notes[note->note()];
1663 boost::shared_ptr<NoteType> old_note = old_rect->note();
1664 old_rect->property_x2() = x;
1665 old_rect->property_outline_what() = (guint32) 0xF;
1667 _active_notes[note->note()] = ev;
1669 /* outline all but right edge */
1670 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1672 /* outline all edges */
1673 ev->property_outline_what() = (guint32) 0xF;
1676 if (update_ghost_regions) {
1677 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1678 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1680 gr->update_note (ev);
1687 MidiRegionView::update_hit (CanvasHit* ev)
1689 boost::shared_ptr<NoteType> note = ev->note();
1691 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1692 const double x = trackview.editor().frame_to_pixel(note_start_frames);
1693 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1694 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1698 return diamond_size;
1701 /** Add a MIDI note to the view (with length).
1703 * If in sustained mode, notes with length 0 will be considered active
1704 * notes, and resolve_note should be called when the corresponding note off
1705 * event arrives, to properly display the note.
1708 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1710 CanvasNoteEvent* event = 0;
1712 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1714 if (midi_view()->note_mode() == Sustained) {
1716 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1718 update_note (ev_rect);
1722 MidiGhostRegion* gr;
1724 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1725 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1726 gr->add_note(ev_rect);
1730 } else if (midi_view()->note_mode() == Percussive) {
1732 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1734 CanvasHit* ev_diamond = new CanvasHit (*this, *_note_group, diamond_size, note);
1736 update_hit (ev_diamond);
1745 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1746 note_selected(event, true);
1749 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1750 event->show_velocity();
1753 event->on_channel_selection_change(_last_channel_selection);
1754 _events.push_back(event);
1763 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1764 MidiStreamView* const view = mtv->midi_view();
1766 view->update_note_range (note->note());
1770 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1771 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1773 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1775 /* potentially extend region to hold new note */
1777 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1778 framepos_t region_end = _region->last_frame();
1780 if (end_frame > region_end) {
1781 _region->set_length (end_frame - _region->position());
1784 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1785 MidiStreamView* const view = mtv->midi_view();
1787 view->update_note_range(new_note->note());
1789 _marked_for_selection.clear ();
1792 start_note_diff_command (_("step add"));
1793 note_diff_add_note (new_note, true, false);
1796 // last_step_edit_note = new_note;
1800 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1802 change_note_lengths (false, false, beats, false, true);
1805 /** Add a new patch change flag to the canvas.
1806 * @param patch the patch change to add
1807 * @param the text to display in the flag
1808 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1811 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool active_channel)
1813 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1814 const double x = trackview.editor().frame_to_pixel (region_frames);
1816 double const height = midi_stream_view()->contents_height();
1818 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1819 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());
1849 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1851 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1852 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1856 if (i != _model->patch_changes().end()) {
1857 key.bank_number = (*i)->bank();
1858 key.program_number = (*i)->program ();
1860 key.bank_number = key.program_number = 0;
1863 if (!key.is_sane()) {
1864 error << string_compose(_("insane MIDI patch key %1:%2"),
1865 key.bank_number, key.program_number) << endmsg;
1870 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1872 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1874 if (pc.patch()->program() != new_patch.program_number) {
1875 c->change_program (pc.patch (), new_patch.program_number);
1878 int const new_bank = new_patch.bank_number;
1879 if (pc.patch()->bank() != new_bank) {
1880 c->change_bank (pc.patch (), new_bank);
1883 _model->apply_command (*trackview.session(), c);
1885 _patch_changes.clear ();
1886 display_patch_changes ();
1890 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1892 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1894 if (old_change->time() != new_change.time()) {
1895 c->change_time (old_change, new_change.time());
1898 if (old_change->channel() != new_change.channel()) {
1899 c->change_channel (old_change, new_change.channel());
1902 if (old_change->program() != new_change.program()) {
1903 c->change_program (old_change, new_change.program());
1906 if (old_change->bank() != new_change.bank()) {
1907 c->change_bank (old_change, new_change.bank());
1910 _model->apply_command (*trackview.session(), c);
1912 _patch_changes.clear ();
1913 display_patch_changes ();
1916 /** Add a patch change to the region.
1917 * @param t Time in frames relative to region position
1918 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1919 * MidiTimeAxisView::get_channel_for_add())
1922 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1924 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1926 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1927 c->add (MidiModel::PatchChangePtr (
1928 new Evoral::PatchChange<Evoral::MusicalTime> (
1929 absolute_frames_to_source_beats (_region->position() + t),
1930 mtv->get_channel_for_add(), patch.program(), patch.bank()
1935 _model->apply_command (*trackview.session(), c);
1937 _patch_changes.clear ();
1938 display_patch_changes ();
1942 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1944 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1945 c->change_time (pc.patch (), t);
1946 _model->apply_command (*trackview.session(), c);
1948 _patch_changes.clear ();
1949 display_patch_changes ();
1953 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1955 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1956 c->remove (pc->patch ());
1957 _model->apply_command (*trackview.session(), c);
1959 _patch_changes.clear ();
1960 display_patch_changes ();
1964 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1966 if (patch.patch()->program() < 127) {
1967 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1968 key.program_number++;
1969 change_patch_change (patch, key);
1974 MidiRegionView::next_patch (CanvasPatchChange& patch)
1976 if (patch.patch()->program() > 0) {
1977 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1978 key.program_number--;
1979 change_patch_change (patch, key);
1984 MidiRegionView::next_bank (CanvasPatchChange& patch)
1986 if (patch.patch()->program() < 127) {
1987 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1988 if (key.bank_number > 0) {
1990 change_patch_change (patch, key);
1996 MidiRegionView::previous_bank (CanvasPatchChange& patch)
1998 if (patch.patch()->program() > 0) {
1999 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2000 if (key.bank_number < 127) {
2002 change_patch_change (patch, key);
2008 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
2010 if (_selection.empty()) {
2014 _selection.erase (cne);
2018 MidiRegionView::delete_selection()
2020 if (_selection.empty()) {
2024 start_note_diff_command (_("delete selection"));
2026 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2027 if ((*i)->selected()) {
2028 _note_diff_command->remove((*i)->note());
2038 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2040 start_note_diff_command (_("delete note"));
2041 _note_diff_command->remove (n);
2044 trackview.editor().verbose_cursor()->hide ();
2048 MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal)
2050 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2052 Selection::iterator tmp = i;
2055 (*i)->set_selected (false);
2056 (*i)->hide_velocity ();
2057 _selection.erase (i);
2065 /* this does not change the status of this regionview w.r.t the editor
2070 SelectionCleared (this); /* EMIT SIGNAL */
2075 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
2077 clear_selection_except (ev);
2079 /* don't bother with checking to see if we should remove this
2080 regionview from the editor selection, since we're about to add
2081 another note, and thus put/keep this regionview in the editor
2085 if (!ev->selected()) {
2086 add_to_selection (ev);
2091 MidiRegionView::select_all_notes ()
2095 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2096 add_to_selection (*i);
2101 MidiRegionView::select_range (framepos_t start, framepos_t end)
2105 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2106 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2107 if (t >= start && t <= end) {
2108 add_to_selection (*i);
2114 MidiRegionView::invert_selection ()
2116 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2117 if ((*i)->selected()) {
2118 remove_from_selection(*i);
2120 add_to_selection (*i);
2126 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2128 uint8_t low_note = 127;
2129 uint8_t high_note = 0;
2130 MidiModel::Notes& notes (_model->notes());
2131 _optimization_iterator = _events.begin();
2137 if (extend && _selection.empty()) {
2143 /* scan existing selection to get note range */
2145 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2146 if ((*i)->note()->note() < low_note) {
2147 low_note = (*i)->note()->note();
2149 if ((*i)->note()->note() > high_note) {
2150 high_note = (*i)->note()->note();
2154 low_note = min (low_note, notenum);
2155 high_note = max (high_note, notenum);
2158 _no_sound_notes = true;
2160 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2162 boost::shared_ptr<NoteType> note (*n);
2163 CanvasNoteEvent* cne;
2164 bool select = false;
2166 if (((1 << note->channel()) & channel_mask) != 0) {
2168 if ((note->note() >= low_note && note->note() <= high_note)) {
2171 } else if (note->note() == notenum) {
2177 if ((cne = find_canvas_note (note)) != 0) {
2178 // extend is false because we've taken care of it,
2179 // since it extends by time range, not pitch.
2180 note_selected (cne, add, false);
2184 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2188 _no_sound_notes = false;
2192 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2194 MidiModel::Notes& notes (_model->notes());
2195 _optimization_iterator = _events.begin();
2197 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2199 boost::shared_ptr<NoteType> note (*n);
2200 CanvasNoteEvent* cne;
2202 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2203 if ((cne = find_canvas_note (note)) != 0) {
2204 if (cne->selected()) {
2205 note_deselected (cne);
2207 note_selected (cne, true, false);
2215 MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2218 clear_selection_except (ev);
2219 if (!_selection.empty()) {
2220 PublicEditor& editor (trackview.editor());
2221 editor.get_selection().add (this);
2227 if (!ev->selected()) {
2228 add_to_selection (ev);
2232 /* find end of latest note selected, select all between that and the start of "ev" */
2234 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2235 Evoral::MusicalTime latest = 0;
2237 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2238 if ((*i)->note()->end_time() > latest) {
2239 latest = (*i)->note()->end_time();
2241 if ((*i)->note()->time() < earliest) {
2242 earliest = (*i)->note()->time();
2246 if (ev->note()->end_time() > latest) {
2247 latest = ev->note()->end_time();
2250 if (ev->note()->time() < earliest) {
2251 earliest = ev->note()->time();
2254 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2256 /* find notes entirely within OR spanning the earliest..latest range */
2258 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2259 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2260 add_to_selection (*i);
2268 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2270 remove_from_selection (ev);
2274 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
2284 // TODO: Make this faster by storing the last updated selection rect, and only
2285 // adjusting things that are in the area that appears/disappeared.
2286 // We probably need a tree to be able to find events in O(log(n)) time.
2288 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2290 /* check if any corner of the note is inside the rect
2293 1) this is computing "touched by", not "contained by" the rect.
2294 2) this does not require that events be sorted in time.
2297 const double ix1 = (*i)->x1();
2298 const double ix2 = (*i)->x2();
2299 const double iy1 = (*i)->y1();
2300 const double iy2 = (*i)->y2();
2302 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2303 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2304 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2305 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2308 if (!(*i)->selected()) {
2309 add_to_selection (*i);
2311 } else if ((*i)->selected() && !extend) {
2312 // Not inside rectangle
2313 remove_from_selection (*i);
2319 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2325 // TODO: Make this faster by storing the last updated selection rect, and only
2326 // adjusting things that are in the area that appears/disappeared.
2327 // We probably need a tree to be able to find events in O(log(n)) time.
2329 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2331 /* check if any corner of the note is inside the rect
2334 1) this is computing "touched by", not "contained by" the rect.
2335 2) this does not require that events be sorted in time.
2338 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2339 // within y- (note-) range
2340 if (!(*i)->selected()) {
2341 add_to_selection (*i);
2343 } else if ((*i)->selected() && !extend) {
2344 // Not inside rectangle
2345 remove_from_selection (*i);
2351 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2353 Selection::iterator i = _selection.find (ev);
2355 if (i != _selection.end()) {
2356 _selection.erase (i);
2359 ev->set_selected (false);
2360 ev->hide_velocity ();
2362 if (_selection.empty()) {
2363 PublicEditor& editor (trackview.editor());
2364 editor.get_selection().remove (this);
2369 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2371 bool add_mrv_selection = false;
2373 if (_selection.empty()) {
2374 add_mrv_selection = true;
2377 if (_selection.insert (ev).second) {
2378 ev->set_selected (true);
2379 start_playing_midi_note ((ev)->note());
2382 if (add_mrv_selection) {
2383 PublicEditor& editor (trackview.editor());
2384 editor.get_selection().add (this);
2389 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2391 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2392 PossibleChord to_play;
2393 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2395 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2396 if ((*i)->note()->time() < earliest) {
2397 earliest = (*i)->note()->time();
2401 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2402 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2403 to_play.push_back ((*i)->note());
2405 (*i)->move_event(dx, dy);
2408 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2410 if (to_play.size() > 1) {
2412 PossibleChord shifted;
2414 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2415 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2416 moved_note->set_note (moved_note->note() + cumulative_dy);
2417 shifted.push_back (moved_note);
2420 start_playing_midi_chord (shifted);
2422 } else if (!to_play.empty()) {
2424 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2425 moved_note->set_note (moved_note->note() + cumulative_dy);
2426 start_playing_midi_note (moved_note);
2432 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2434 uint8_t lowest_note_in_selection = 127;
2435 uint8_t highest_note_in_selection = 0;
2436 uint8_t highest_note_difference = 0;
2438 // find highest and lowest notes first
2440 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2441 uint8_t pitch = (*i)->note()->note();
2442 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2443 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2447 cerr << "dnote: " << (int) dnote << endl;
2448 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2449 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2450 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2451 << int(highest_note_in_selection) << endl;
2452 cerr << "selection size: " << _selection.size() << endl;
2453 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2456 // Make sure the note pitch does not exceed the MIDI standard range
2457 if (highest_note_in_selection + dnote > 127) {
2458 highest_note_difference = highest_note_in_selection - 127;
2461 start_note_diff_command (_("move notes"));
2463 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2465 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2466 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2472 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2474 uint8_t original_pitch = (*i)->note()->note();
2475 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2477 // keep notes in standard midi range
2478 clamp_to_0_127(new_pitch);
2480 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2481 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2483 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2488 // care about notes being moved beyond the upper/lower bounds on the canvas
2489 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2490 highest_note_in_selection > midi_stream_view()->highest_note()) {
2491 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2495 /** @param x Pixel relative to the region position.
2496 * @return Snapped frame relative to the region position.
2499 MidiRegionView::snap_pixel_to_frame(double x)
2501 PublicEditor& editor (trackview.editor());
2502 return snap_frame_to_frame (editor.pixel_to_frame (x));
2505 /** @param x Pixel relative to the region position.
2506 * @return Snapped pixel relative to the region position.
2509 MidiRegionView::snap_to_pixel(double x)
2511 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2515 MidiRegionView::get_position_pixels()
2517 framepos_t region_frame = get_position();
2518 return trackview.editor().frame_to_pixel(region_frame);
2522 MidiRegionView::get_end_position_pixels()
2524 framepos_t frame = get_position() + get_duration ();
2525 return trackview.editor().frame_to_pixel(frame);
2529 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2531 /* the time converter will return the frame corresponding to `beats'
2532 relative to the start of the source. The start of the source
2533 is an implied position given by region->position - region->start
2535 const framepos_t source_start = _region->position() - _region->start();
2536 return source_start + _source_relative_time_converter.to (beats);
2540 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2542 /* the `frames' argument needs to be converted into a frame count
2543 relative to the start of the source before being passed in to the
2546 const framepos_t source_start = _region->position() - _region->start();
2547 return _source_relative_time_converter.from (frames - source_start);
2551 MidiRegionView::region_beats_to_region_frames(double beats) const
2553 return _region_relative_time_converter.to(beats);
2557 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2559 return _region_relative_time_converter.from(frames);
2563 MidiRegionView::begin_resizing (bool /*at_front*/)
2565 _resize_data.clear();
2567 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2568 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2570 // only insert CanvasNotes into the map
2572 NoteResizeData *resize_data = new NoteResizeData();
2573 resize_data->canvas_note = note;
2575 // create a new SimpleRect from the note which will be the resize preview
2576 SimpleRect *resize_rect = new SimpleRect(
2577 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2579 // calculate the colors: get the color settings
2580 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2581 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2584 // make the resize preview notes more transparent and bright
2585 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2587 // calculate color based on note velocity
2588 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2589 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2593 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2594 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2596 resize_data->resize_rect = resize_rect;
2597 _resize_data.push_back(resize_data);
2602 /** Update resizing notes while user drags.
2603 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2604 * @param at_front which end of the note (true == note on, false == note off)
2605 * @param delta_x change in mouse position since the start of the drag
2606 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2607 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2608 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2609 * as the \a primary note.
2612 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2614 bool cursor_set = false;
2616 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2617 SimpleRect* resize_rect = (*i)->resize_rect;
2618 CanvasNote* canvas_note = (*i)->canvas_note;
2623 current_x = canvas_note->x1() + delta_x;
2625 current_x = primary->x1() + delta_x;
2629 current_x = canvas_note->x2() + delta_x;
2631 current_x = primary->x2() + delta_x;
2636 resize_rect->property_x1() = snap_to_pixel(current_x);
2637 resize_rect->property_x2() = canvas_note->x2();
2639 resize_rect->property_x2() = snap_to_pixel(current_x);
2640 resize_rect->property_x1() = canvas_note->x1();
2646 beats = snap_pixel_to_frame (current_x);
2647 beats = region_frames_to_region_beats (beats);
2652 if (beats < canvas_note->note()->end_time()) {
2653 len = canvas_note->note()->time() - beats;
2654 len += canvas_note->note()->length();
2659 if (beats >= canvas_note->note()->time()) {
2660 len = beats - canvas_note->note()->time();
2667 snprintf (buf, sizeof (buf), "%.3g beats", len);
2668 show_verbose_cursor (buf, 0, 0);
2677 /** Finish resizing notes when the user releases the mouse button.
2678 * Parameters the same as for \a update_resizing().
2681 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2683 start_note_diff_command (_("resize notes"));
2685 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2686 CanvasNote* canvas_note = (*i)->canvas_note;
2687 SimpleRect* resize_rect = (*i)->resize_rect;
2689 /* Get the new x position for this resize, which is in pixels relative
2690 * to the region position.
2697 current_x = canvas_note->x1() + delta_x;
2699 current_x = primary->x1() + delta_x;
2703 current_x = canvas_note->x2() + delta_x;
2705 current_x = primary->x2() + delta_x;
2709 /* Convert that to a frame within the source */
2710 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2712 /* and then to beats */
2713 current_x = region_frames_to_region_beats (current_x);
2715 if (at_front && current_x < canvas_note->note()->end_time()) {
2716 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2718 double len = canvas_note->note()->time() - current_x;
2719 len += canvas_note->note()->length();
2722 /* XXX convert to beats */
2723 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2728 double len = current_x - canvas_note->note()->time();
2731 /* XXX convert to beats */
2732 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2740 _resize_data.clear();
2745 MidiRegionView::abort_resizing ()
2747 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2748 delete (*i)->resize_rect;
2752 _resize_data.clear ();
2756 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2758 uint8_t new_velocity;
2761 new_velocity = event->note()->velocity() + velocity;
2762 clamp_to_0_127(new_velocity);
2764 new_velocity = velocity;
2767 event->set_selected (event->selected()); // change color
2769 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2773 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2778 new_note = event->note()->note() + note;
2783 clamp_to_0_127 (new_note);
2784 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2788 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2790 bool change_start = false;
2791 bool change_length = false;
2792 Evoral::MusicalTime new_start = 0;
2793 Evoral::MusicalTime new_length = 0;
2795 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2797 front_delta: if positive - move the start of the note later in time (shortening it)
2798 if negative - move the start of the note earlier in time (lengthening it)
2800 end_delta: if positive - move the end of the note later in time (lengthening it)
2801 if negative - move the end of the note earlier in time (shortening it)
2805 if (front_delta < 0) {
2807 if (event->note()->time() < -front_delta) {
2810 new_start = event->note()->time() + front_delta; // moves earlier
2813 /* start moved toward zero, so move the end point out to where it used to be.
2814 Note that front_delta is negative, so this increases the length.
2817 new_length = event->note()->length() - front_delta;
2818 change_start = true;
2819 change_length = true;
2823 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2825 if (new_pos < event->note()->end_time()) {
2826 new_start = event->note()->time() + front_delta;
2827 /* start moved toward the end, so move the end point back to where it used to be */
2828 new_length = event->note()->length() - front_delta;
2829 change_start = true;
2830 change_length = true;
2837 bool can_change = true;
2838 if (end_delta < 0) {
2839 if (event->note()->length() < -end_delta) {
2845 new_length = event->note()->length() + end_delta;
2846 change_length = true;
2851 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2854 if (change_length) {
2855 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2860 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2862 uint8_t new_channel;
2866 if (event->note()->channel() < -chn) {
2869 new_channel = event->note()->channel() + chn;
2872 new_channel = event->note()->channel() + chn;
2875 new_channel = (uint8_t) chn;
2878 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2882 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2884 Evoral::MusicalTime new_time;
2888 if (event->note()->time() < -delta) {
2891 new_time = event->note()->time() + delta;
2894 new_time = event->note()->time() + delta;
2900 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2904 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2906 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2910 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2915 if (_selection.empty()) {
2930 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2931 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2937 start_note_diff_command (_("change velocities"));
2939 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2940 Selection::iterator next = i;
2944 if (i == _selection.begin()) {
2945 change_note_velocity (*i, delta, true);
2946 value = (*i)->note()->velocity() + delta;
2948 change_note_velocity (*i, value, false);
2952 change_note_velocity (*i, delta, true);
2961 if (!_selection.empty()) {
2963 snprintf (buf, sizeof (buf), "Vel %d",
2964 (int) (*_selection.begin())->note()->velocity());
2965 show_verbose_cursor (buf, 10, 10);
2971 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2973 if (_selection.empty()) {
2990 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2992 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2996 if ((int8_t) (*i)->note()->note() + delta > 127) {
3003 start_note_diff_command (_("transpose"));
3005 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3006 Selection::iterator next = i;
3008 change_note_note (*i, delta, true);
3016 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3022 /* grab the current grid distance */
3024 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3026 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3027 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3037 start_note_diff_command (_("change note lengths"));
3039 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3040 Selection::iterator next = i;
3043 /* note the negation of the delta for start */
3045 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3054 MidiRegionView::nudge_notes (bool forward)
3056 if (_selection.empty()) {
3060 /* pick a note as the point along the timeline to get the nudge distance.
3061 its not necessarily the earliest note, so we may want to pull the notes out
3062 into a vector and sort before using the first one.
3065 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3067 framecnt_t distance;
3069 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3071 /* grid is off - use nudge distance */
3073 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3079 framepos_t next_pos = ref_point;
3082 if (max_framepos - 1 < next_pos) {
3086 if (next_pos == 0) {
3092 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3093 distance = ref_point - next_pos;
3096 if (distance == 0) {
3100 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
3106 start_note_diff_command (_("nudge"));
3108 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3109 Selection::iterator next = i;
3111 change_note_time (*i, delta, true);
3119 MidiRegionView::change_channel(uint8_t channel)
3121 start_note_diff_command(_("change channel"));
3122 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3123 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3131 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
3133 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3135 pre_enter_cursor = editor->get_canvas_cursor ();
3137 if (_mouse_state == SelectTouchDragging) {
3138 note_selected (ev, true);
3141 show_verbose_cursor (ev->note ());
3145 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
3147 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3149 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3150 (*i)->hide_velocity ();
3153 editor->verbose_cursor()->hide ();
3155 if (pre_enter_cursor) {
3156 editor->set_canvas_cursor (pre_enter_cursor);
3157 pre_enter_cursor = 0;
3162 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* p)
3165 /* XXX should get patch name if we can */
3166 s << _("Bank:") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3167 << _("Program:") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3168 << _("Channel:") << ((int) p->patch()->channel() + 1);
3169 show_verbose_cursor (s.str(), 10, 20);
3174 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
3176 trackview.editor().verbose_cursor()->hide ();
3177 /* focus will transfer back via the enter-notify event sent to this
3183 MidiRegionView::sysex_entered (ArdourCanvas::CanvasSysEx* p)
3187 show_verbose_cursor (s.str(), 10, 20);
3192 MidiRegionView::sysex_left (ArdourCanvas::CanvasSysEx *)
3194 trackview.editor().verbose_cursor()->hide ();
3195 /* focus will transfer back via the enter-notify event sent to this
3201 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3203 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3204 Editing::MouseMode mm = editor->current_mouse_mode();
3205 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3207 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3208 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3209 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3210 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3212 if (pre_enter_cursor && can_set_cursor) {
3213 editor->set_canvas_cursor (pre_enter_cursor);
3219 MidiRegionView::set_frame_color()
3223 TimeAxisViewItem::set_frame_color ();
3230 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3231 } else if (high_enough_for_name) {
3232 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3237 if (!rect_visible) {
3238 f = UINT_RGBA_CHANGE_A (f, 0);
3241 frame->property_fill_color_rgba() = f;
3245 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3247 if (mode == ForceChannel) {
3248 mask = 0xFFFF; // Show all notes as active (below)
3251 // Update notes for selection
3252 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3253 (*i)->on_channel_selection_change(mask);
3256 _last_channel_selection = mask;
3257 _last_channel_mode = mode;
3259 _patch_changes.clear ();
3260 display_patch_changes ();
3264 MidiRegionView::instrument_settings_changed ()
3270 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3272 if (_selection.empty()) {
3276 PublicEditor& editor (trackview.editor());
3280 /* XXX what to do ? */
3284 editor.get_cut_buffer().add (selection_as_cut_buffer());
3292 start_note_diff_command();
3294 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3301 note_diff_remove_note (*i);
3311 MidiRegionView::selection_as_cut_buffer () const
3315 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3316 NoteType* n = (*i)->note().get();
3317 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3320 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3326 /** This method handles undo */
3328 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3334 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3336 trackview.session()->begin_reversible_command (_("paste"));
3338 start_note_diff_command (_("paste"));
3340 Evoral::MusicalTime beat_delta;
3341 Evoral::MusicalTime paste_pos_beats;
3342 Evoral::MusicalTime duration;
3343 Evoral::MusicalTime end_point = 0;
3345 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3346 paste_pos_beats = absolute_frames_to_source_beats (pos);
3347 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3348 paste_pos_beats = 0;
3350 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",
3351 (*mcb.notes().begin())->time(),
3352 (*mcb.notes().rbegin())->end_time(),
3353 duration, pos, _region->position(),
3354 paste_pos_beats, beat_delta));
3358 for (int n = 0; n < (int) times; ++n) {
3360 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3362 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3363 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3365 /* make all newly added notes selected */
3367 note_diff_add_note (copied_note, true);
3368 end_point = copied_note->end_time();
3371 paste_pos_beats += duration;
3374 /* if we pasted past the current end of the region, extend the region */
3376 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3377 framepos_t region_end = _region->position() + _region->length() - 1;
3379 if (end_frame > region_end) {
3381 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3383 _region->clear_changes ();
3384 _region->set_length (end_frame - _region->position());
3385 trackview.session()->add_command (new StatefulDiffCommand (_region));
3390 trackview.session()->commit_reversible_command ();
3393 struct EventNoteTimeEarlyFirstComparator {
3394 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3395 return a->note()->time() < b->note()->time();
3400 MidiRegionView::time_sort_events ()
3402 if (!_sort_needed) {
3406 EventNoteTimeEarlyFirstComparator cmp;
3409 _sort_needed = false;
3413 MidiRegionView::goto_next_note (bool add_to_selection)
3415 bool use_next = false;
3417 if (_events.back()->selected()) {
3421 time_sort_events ();
3423 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3424 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3426 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3427 if ((*i)->selected()) {
3430 } else if (use_next) {
3431 if (channel_mask & (1 << (*i)->note()->channel())) {
3432 if (!add_to_selection) {
3435 note_selected (*i, true, false);
3442 /* use the first one */
3444 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3445 unique_select (_events.front());
3450 MidiRegionView::goto_previous_note (bool add_to_selection)
3452 bool use_next = false;
3454 if (_events.front()->selected()) {
3458 time_sort_events ();
3460 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3461 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3463 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3464 if ((*i)->selected()) {
3467 } else if (use_next) {
3468 if (channel_mask & (1 << (*i)->note()->channel())) {
3469 if (!add_to_selection) {
3472 note_selected (*i, true, false);
3479 /* use the last one */
3481 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3482 unique_select (*(_events.rbegin()));
3487 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3489 bool had_selected = false;
3491 time_sort_events ();
3493 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3494 if ((*i)->selected()) {
3495 selected.insert ((*i)->note());
3496 had_selected = true;
3500 if (allow_all_if_none_selected && !had_selected) {
3501 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3502 selected.insert ((*i)->note());
3508 MidiRegionView::update_ghost_note (double x, double y)
3510 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3515 _note_group->w2i (x, y);
3517 PublicEditor& editor = trackview.editor ();
3519 framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3520 framecnt_t grid_frames;
3521 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3523 /* use region_frames... because we are converting a delta within the region
3527 double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3533 /* note that this sets the time of the ghost note in beats relative to
3534 the start of the source; that is how all note times are stored.
3536 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3537 _ghost_note->note()->set_length (length);
3538 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3539 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3541 /* the ghost note does not appear in ghost regions, so pass false in here */
3542 update_note (_ghost_note, false);
3544 show_verbose_cursor (_ghost_note->note ());
3548 MidiRegionView::create_ghost_note (double x, double y)
3550 remove_ghost_note ();
3552 boost::shared_ptr<NoteType> g (new NoteType);
3553 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3554 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3555 update_ghost_note (x, y);
3556 _ghost_note->show ();
3561 show_verbose_cursor (_ghost_note->note ());
3565 MidiRegionView::snap_changed ()
3571 create_ghost_note (_last_ghost_x, _last_ghost_y);
3575 MidiRegionView::drop_down_keys ()
3577 _mouse_state = None;
3581 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3583 double note = midi_stream_view()->y_to_note(y);
3585 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3587 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3589 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3590 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3591 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3592 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3597 bool add_mrv_selection = false;
3599 if (_selection.empty()) {
3600 add_mrv_selection = true;
3603 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3604 if (_selection.insert (*i).second) {
3605 (*i)->set_selected (true);
3609 if (add_mrv_selection) {
3610 PublicEditor& editor (trackview.editor());
3611 editor.get_selection().add (this);
3616 MidiRegionView::color_handler ()
3618 RegionView::color_handler ();
3620 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3621 (*i)->set_selected ((*i)->selected()); // will change color
3624 /* XXX probably more to do here */
3628 MidiRegionView::enable_display (bool yn)
3630 RegionView::enable_display (yn);
3637 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3639 if (_step_edit_cursor == 0) {
3640 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3642 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3643 _step_edit_cursor->property_y1() = 0;
3644 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3645 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3646 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3649 move_step_edit_cursor (pos);
3650 _step_edit_cursor->show ();
3654 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3656 _step_edit_cursor_position = pos;
3658 if (_step_edit_cursor) {
3659 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3660 _step_edit_cursor->property_x1() = pixel;
3661 set_step_edit_cursor_width (_step_edit_cursor_width);
3666 MidiRegionView::hide_step_edit_cursor ()
3668 if (_step_edit_cursor) {
3669 _step_edit_cursor->hide ();
3674 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3676 _step_edit_cursor_width = beats;
3678 if (_step_edit_cursor) {
3679 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3683 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3684 * @param w Source that the data will end up in.
3687 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3689 if (!_active_notes) {
3690 /* we aren't actively being recorded to */
3694 boost::shared_ptr<MidiSource> src = w.lock ();
3695 if (!src || src != midi_region()->midi_source()) {
3696 /* recorded data was not destined for our source */
3700 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3702 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3704 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3706 framepos_t back = max_framepos;
3708 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3709 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3711 if (ev.is_channel_event()) {
3712 if (_last_channel_mode == FilterChannels) {
3713 if (((uint16_t(1) << ev.channel()) & _last_channel_selection) == 0) {
3719 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3720 frames from the start of the source, and so time_beats is in terms of the
3724 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3726 if (ev.type() == MIDI_CMD_NOTE_ON) {
3728 boost::shared_ptr<NoteType> note (
3729 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3732 add_note (note, true);
3734 /* fix up our note range */
3735 if (ev.note() < _current_range_min) {
3736 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3737 } else if (ev.note() > _current_range_max) {
3738 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3741 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3742 resolve_note (ev.note (), time_beats);
3748 midi_stream_view()->check_record_layers (region(), back);
3752 MidiRegionView::trim_front_starting ()
3754 /* Reparent the note group to the region view's parent, so that it doesn't change
3755 when the region view is trimmed.
3757 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3758 _temporary_note_group->move (group->property_x(), group->property_y());
3759 _note_group->reparent (*_temporary_note_group);
3763 MidiRegionView::trim_front_ending ()
3765 _note_group->reparent (*group);
3766 delete _temporary_note_group;
3767 _temporary_note_group = 0;
3769 if (_region->start() < 0) {
3770 /* Trim drag made start time -ve; fix this */
3771 midi_region()->fix_negative_start ();
3776 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3778 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3780 d.set_position (Gtk::WIN_POS_MOUSE);
3782 int response = d.run();
3785 case Gtk::RESPONSE_ACCEPT:
3787 case Gtk::RESPONSE_REJECT:
3788 delete_patch_change (pc);
3794 change_patch_change (pc->patch(), d.patch ());
3798 MidiRegionView::delete_sysex (CanvasSysEx* sysex)
3800 MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3801 c->remove (sysex->sysex());
3802 _model->apply_command (*trackview.session(), c);
3809 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3811 using namespace MIDI::Name;
3815 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3817 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3819 MIDI::Name::PatchPrimaryKey patch_key;
3820 get_patch_key_at(n->time(), n->channel(), patch_key);
3821 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3823 patch_key.bank_number,
3824 patch_key.program_number,
3830 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3832 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3833 (int) n->channel() + 1,
3834 (int) n->velocity());
3836 show_verbose_cursor(buf, 10, 20);
3840 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3844 trackview.editor().get_pointer_position (wx, wy);
3849 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3851 double x1, y1, x2, y2;
3852 trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3854 if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3855 wy -= (y2 - y1) + 2 * yoffset;
3858 trackview.editor().verbose_cursor()->set (text, wx, wy);
3859 trackview.editor().verbose_cursor()->show ();
3862 /** @param p A session framepos.
3863 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3864 * @return p snapped to the grid subdivision underneath it.
3867 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3869 PublicEditor& editor = trackview.editor ();
3872 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3878 grid_frames = region_beats_to_region_frames (grid_beats);
3880 /* Hack so that we always snap to the note that we are over, instead of snapping
3881 to the next one if we're more than halfway through the one we're over.
3883 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3884 p -= grid_frames / 2;
3887 return snap_frame_to_frame (p);
3890 /** Called when the selection has been cleared in any MidiRegionView.
3891 * @param rv MidiRegionView that the selection was cleared in.
3894 MidiRegionView::selection_cleared (MidiRegionView* rv)
3900 /* Clear our selection in sympathy; but don't signal the fact */
3901 clear_selection (false);
3905 MidiRegionView::note_button_release ()
3907 delete _note_player;