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());
1848 /// Return true iff @p pc applies to the given time on the given channel.
1850 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel)
1852 return pc->time() <= time && pc->channel() == channel;
1856 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1858 // The earliest event not before time
1859 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1861 // Go backwards until we find the latest PC for this channel, or the start
1862 while (i != _model->patch_changes().begin() &&
1863 (i == _model->patch_changes().end() ||
1864 !patch_applies(*i, time, channel))) {
1868 if (patch_applies(*i, time, channel)) {
1869 key.bank_number = (*i)->bank();
1870 key.program_number = (*i)->program ();
1872 key.bank_number = key.program_number = 0;
1875 if (!key.is_sane()) {
1876 error << string_compose(_("insane MIDI patch key %1:%2"),
1877 key.bank_number, key.program_number) << endmsg;
1882 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1884 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1886 if (pc.patch()->program() != new_patch.program_number) {
1887 c->change_program (pc.patch (), new_patch.program_number);
1890 int const new_bank = new_patch.bank_number;
1891 if (pc.patch()->bank() != new_bank) {
1892 c->change_bank (pc.patch (), new_bank);
1895 _model->apply_command (*trackview.session(), c);
1897 _patch_changes.clear ();
1898 display_patch_changes ();
1902 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1904 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1906 if (old_change->time() != new_change.time()) {
1907 c->change_time (old_change, new_change.time());
1910 if (old_change->channel() != new_change.channel()) {
1911 c->change_channel (old_change, new_change.channel());
1914 if (old_change->program() != new_change.program()) {
1915 c->change_program (old_change, new_change.program());
1918 if (old_change->bank() != new_change.bank()) {
1919 c->change_bank (old_change, new_change.bank());
1922 _model->apply_command (*trackview.session(), c);
1924 _patch_changes.clear ();
1925 display_patch_changes ();
1928 /** Add a patch change to the region.
1929 * @param t Time in frames relative to region position
1930 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1931 * MidiTimeAxisView::get_channel_for_add())
1934 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1936 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1938 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1939 c->add (MidiModel::PatchChangePtr (
1940 new Evoral::PatchChange<Evoral::MusicalTime> (
1941 absolute_frames_to_source_beats (_region->position() + t),
1942 mtv->get_channel_for_add(), patch.program(), patch.bank()
1947 _model->apply_command (*trackview.session(), c);
1949 _patch_changes.clear ();
1950 display_patch_changes ();
1954 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1956 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1957 c->change_time (pc.patch (), t);
1958 _model->apply_command (*trackview.session(), c);
1960 _patch_changes.clear ();
1961 display_patch_changes ();
1965 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1967 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1968 c->remove (pc->patch ());
1969 _model->apply_command (*trackview.session(), c);
1971 _patch_changes.clear ();
1972 display_patch_changes ();
1976 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1978 if (patch.patch()->program() < 127) {
1979 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1980 key.program_number++;
1981 change_patch_change (patch, key);
1986 MidiRegionView::next_patch (CanvasPatchChange& patch)
1988 if (patch.patch()->program() > 0) {
1989 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1990 key.program_number--;
1991 change_patch_change (patch, key);
1996 MidiRegionView::next_bank (CanvasPatchChange& patch)
1998 if (patch.patch()->program() < 127) {
1999 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2000 if (key.bank_number > 0) {
2002 change_patch_change (patch, key);
2008 MidiRegionView::previous_bank (CanvasPatchChange& patch)
2010 if (patch.patch()->program() > 0) {
2011 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2012 if (key.bank_number < 127) {
2014 change_patch_change (patch, key);
2020 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
2022 if (_selection.empty()) {
2026 _selection.erase (cne);
2030 MidiRegionView::delete_selection()
2032 if (_selection.empty()) {
2036 start_note_diff_command (_("delete selection"));
2038 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2039 if ((*i)->selected()) {
2040 _note_diff_command->remove((*i)->note());
2050 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2052 start_note_diff_command (_("delete note"));
2053 _note_diff_command->remove (n);
2056 trackview.editor().verbose_cursor()->hide ();
2060 MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal)
2062 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2064 Selection::iterator tmp = i;
2067 (*i)->set_selected (false);
2068 (*i)->hide_velocity ();
2069 _selection.erase (i);
2077 /* this does not change the status of this regionview w.r.t the editor
2082 SelectionCleared (this); /* EMIT SIGNAL */
2087 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
2089 clear_selection_except (ev);
2091 /* don't bother with checking to see if we should remove this
2092 regionview from the editor selection, since we're about to add
2093 another note, and thus put/keep this regionview in the editor
2097 if (!ev->selected()) {
2098 add_to_selection (ev);
2103 MidiRegionView::select_all_notes ()
2107 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2108 add_to_selection (*i);
2113 MidiRegionView::select_range (framepos_t start, framepos_t end)
2117 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2118 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2119 if (t >= start && t <= end) {
2120 add_to_selection (*i);
2126 MidiRegionView::invert_selection ()
2128 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2129 if ((*i)->selected()) {
2130 remove_from_selection(*i);
2132 add_to_selection (*i);
2138 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2140 uint8_t low_note = 127;
2141 uint8_t high_note = 0;
2142 MidiModel::Notes& notes (_model->notes());
2143 _optimization_iterator = _events.begin();
2149 if (extend && _selection.empty()) {
2155 /* scan existing selection to get note range */
2157 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2158 if ((*i)->note()->note() < low_note) {
2159 low_note = (*i)->note()->note();
2161 if ((*i)->note()->note() > high_note) {
2162 high_note = (*i)->note()->note();
2166 low_note = min (low_note, notenum);
2167 high_note = max (high_note, notenum);
2170 _no_sound_notes = true;
2172 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2174 boost::shared_ptr<NoteType> note (*n);
2175 CanvasNoteEvent* cne;
2176 bool select = false;
2178 if (((1 << note->channel()) & channel_mask) != 0) {
2180 if ((note->note() >= low_note && note->note() <= high_note)) {
2183 } else if (note->note() == notenum) {
2189 if ((cne = find_canvas_note (note)) != 0) {
2190 // extend is false because we've taken care of it,
2191 // since it extends by time range, not pitch.
2192 note_selected (cne, add, false);
2196 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2200 _no_sound_notes = false;
2204 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2206 MidiModel::Notes& notes (_model->notes());
2207 _optimization_iterator = _events.begin();
2209 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2211 boost::shared_ptr<NoteType> note (*n);
2212 CanvasNoteEvent* cne;
2214 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2215 if ((cne = find_canvas_note (note)) != 0) {
2216 if (cne->selected()) {
2217 note_deselected (cne);
2219 note_selected (cne, true, false);
2227 MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2230 clear_selection_except (ev);
2231 if (!_selection.empty()) {
2232 PublicEditor& editor (trackview.editor());
2233 editor.get_selection().add (this);
2239 if (!ev->selected()) {
2240 add_to_selection (ev);
2244 /* find end of latest note selected, select all between that and the start of "ev" */
2246 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2247 Evoral::MusicalTime latest = 0;
2249 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2250 if ((*i)->note()->end_time() > latest) {
2251 latest = (*i)->note()->end_time();
2253 if ((*i)->note()->time() < earliest) {
2254 earliest = (*i)->note()->time();
2258 if (ev->note()->end_time() > latest) {
2259 latest = ev->note()->end_time();
2262 if (ev->note()->time() < earliest) {
2263 earliest = ev->note()->time();
2266 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2268 /* find notes entirely within OR spanning the earliest..latest range */
2270 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2271 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2272 add_to_selection (*i);
2280 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2282 remove_from_selection (ev);
2286 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
2296 // TODO: Make this faster by storing the last updated selection rect, and only
2297 // adjusting things that are in the area that appears/disappeared.
2298 // We probably need a tree to be able to find events in O(log(n)) time.
2300 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2302 /* check if any corner of the note is inside the rect
2305 1) this is computing "touched by", not "contained by" the rect.
2306 2) this does not require that events be sorted in time.
2309 const double ix1 = (*i)->x1();
2310 const double ix2 = (*i)->x2();
2311 const double iy1 = (*i)->y1();
2312 const double iy2 = (*i)->y2();
2314 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2315 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2316 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2317 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2320 if (!(*i)->selected()) {
2321 add_to_selection (*i);
2323 } else if ((*i)->selected() && !extend) {
2324 // Not inside rectangle
2325 remove_from_selection (*i);
2331 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2337 // TODO: Make this faster by storing the last updated selection rect, and only
2338 // adjusting things that are in the area that appears/disappeared.
2339 // We probably need a tree to be able to find events in O(log(n)) time.
2341 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2343 /* check if any corner of the note is inside the rect
2346 1) this is computing "touched by", not "contained by" the rect.
2347 2) this does not require that events be sorted in time.
2350 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2351 // within y- (note-) range
2352 if (!(*i)->selected()) {
2353 add_to_selection (*i);
2355 } else if ((*i)->selected() && !extend) {
2356 // Not inside rectangle
2357 remove_from_selection (*i);
2363 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2365 Selection::iterator i = _selection.find (ev);
2367 if (i != _selection.end()) {
2368 _selection.erase (i);
2371 ev->set_selected (false);
2372 ev->hide_velocity ();
2374 if (_selection.empty()) {
2375 PublicEditor& editor (trackview.editor());
2376 editor.get_selection().remove (this);
2381 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2383 bool add_mrv_selection = false;
2385 if (_selection.empty()) {
2386 add_mrv_selection = true;
2389 if (_selection.insert (ev).second) {
2390 ev->set_selected (true);
2391 start_playing_midi_note ((ev)->note());
2394 if (add_mrv_selection) {
2395 PublicEditor& editor (trackview.editor());
2396 editor.get_selection().add (this);
2401 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2403 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2404 PossibleChord to_play;
2405 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2407 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2408 if ((*i)->note()->time() < earliest) {
2409 earliest = (*i)->note()->time();
2413 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2414 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2415 to_play.push_back ((*i)->note());
2417 (*i)->move_event(dx, dy);
2420 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2422 if (to_play.size() > 1) {
2424 PossibleChord shifted;
2426 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2427 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2428 moved_note->set_note (moved_note->note() + cumulative_dy);
2429 shifted.push_back (moved_note);
2432 start_playing_midi_chord (shifted);
2434 } else if (!to_play.empty()) {
2436 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2437 moved_note->set_note (moved_note->note() + cumulative_dy);
2438 start_playing_midi_note (moved_note);
2444 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2446 uint8_t lowest_note_in_selection = 127;
2447 uint8_t highest_note_in_selection = 0;
2448 uint8_t highest_note_difference = 0;
2450 // find highest and lowest notes first
2452 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2453 uint8_t pitch = (*i)->note()->note();
2454 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2455 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2459 cerr << "dnote: " << (int) dnote << endl;
2460 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2461 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2462 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2463 << int(highest_note_in_selection) << endl;
2464 cerr << "selection size: " << _selection.size() << endl;
2465 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2468 // Make sure the note pitch does not exceed the MIDI standard range
2469 if (highest_note_in_selection + dnote > 127) {
2470 highest_note_difference = highest_note_in_selection - 127;
2473 start_note_diff_command (_("move notes"));
2475 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2477 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2478 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2484 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2486 uint8_t original_pitch = (*i)->note()->note();
2487 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2489 // keep notes in standard midi range
2490 clamp_to_0_127(new_pitch);
2492 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2493 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2495 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2500 // care about notes being moved beyond the upper/lower bounds on the canvas
2501 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2502 highest_note_in_selection > midi_stream_view()->highest_note()) {
2503 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2507 /** @param x Pixel relative to the region position.
2508 * @return Snapped frame relative to the region position.
2511 MidiRegionView::snap_pixel_to_frame(double x)
2513 PublicEditor& editor (trackview.editor());
2514 return snap_frame_to_frame (editor.pixel_to_frame (x));
2517 /** @param x Pixel relative to the region position.
2518 * @return Snapped pixel relative to the region position.
2521 MidiRegionView::snap_to_pixel(double x)
2523 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2527 MidiRegionView::get_position_pixels()
2529 framepos_t region_frame = get_position();
2530 return trackview.editor().frame_to_pixel(region_frame);
2534 MidiRegionView::get_end_position_pixels()
2536 framepos_t frame = get_position() + get_duration ();
2537 return trackview.editor().frame_to_pixel(frame);
2541 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2543 /* the time converter will return the frame corresponding to `beats'
2544 relative to the start of the source. The start of the source
2545 is an implied position given by region->position - region->start
2547 const framepos_t source_start = _region->position() - _region->start();
2548 return source_start + _source_relative_time_converter.to (beats);
2552 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2554 /* the `frames' argument needs to be converted into a frame count
2555 relative to the start of the source before being passed in to the
2558 const framepos_t source_start = _region->position() - _region->start();
2559 return _source_relative_time_converter.from (frames - source_start);
2563 MidiRegionView::region_beats_to_region_frames(double beats) const
2565 return _region_relative_time_converter.to(beats);
2569 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2571 return _region_relative_time_converter.from(frames);
2575 MidiRegionView::begin_resizing (bool /*at_front*/)
2577 _resize_data.clear();
2579 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2580 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2582 // only insert CanvasNotes into the map
2584 NoteResizeData *resize_data = new NoteResizeData();
2585 resize_data->canvas_note = note;
2587 // create a new SimpleRect from the note which will be the resize preview
2588 SimpleRect *resize_rect = new SimpleRect(
2589 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2591 // calculate the colors: get the color settings
2592 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2593 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2596 // make the resize preview notes more transparent and bright
2597 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2599 // calculate color based on note velocity
2600 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2601 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2605 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2606 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2608 resize_data->resize_rect = resize_rect;
2609 _resize_data.push_back(resize_data);
2614 /** Update resizing notes while user drags.
2615 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2616 * @param at_front which end of the note (true == note on, false == note off)
2617 * @param delta_x change in mouse position since the start of the drag
2618 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2619 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2620 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2621 * as the \a primary note.
2624 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2626 bool cursor_set = false;
2628 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2629 SimpleRect* resize_rect = (*i)->resize_rect;
2630 CanvasNote* canvas_note = (*i)->canvas_note;
2635 current_x = canvas_note->x1() + delta_x;
2637 current_x = primary->x1() + delta_x;
2641 current_x = canvas_note->x2() + delta_x;
2643 current_x = primary->x2() + delta_x;
2648 resize_rect->property_x1() = snap_to_pixel(current_x);
2649 resize_rect->property_x2() = canvas_note->x2();
2651 resize_rect->property_x2() = snap_to_pixel(current_x);
2652 resize_rect->property_x1() = canvas_note->x1();
2658 beats = snap_pixel_to_frame (current_x);
2659 beats = region_frames_to_region_beats (beats);
2664 if (beats < canvas_note->note()->end_time()) {
2665 len = canvas_note->note()->time() - beats;
2666 len += canvas_note->note()->length();
2671 if (beats >= canvas_note->note()->time()) {
2672 len = beats - canvas_note->note()->time();
2679 snprintf (buf, sizeof (buf), "%.3g beats", len);
2680 show_verbose_cursor (buf, 0, 0);
2689 /** Finish resizing notes when the user releases the mouse button.
2690 * Parameters the same as for \a update_resizing().
2693 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2695 start_note_diff_command (_("resize notes"));
2697 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2698 CanvasNote* canvas_note = (*i)->canvas_note;
2699 SimpleRect* resize_rect = (*i)->resize_rect;
2701 /* Get the new x position for this resize, which is in pixels relative
2702 * to the region position.
2709 current_x = canvas_note->x1() + delta_x;
2711 current_x = primary->x1() + delta_x;
2715 current_x = canvas_note->x2() + delta_x;
2717 current_x = primary->x2() + delta_x;
2721 /* Convert that to a frame within the source */
2722 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2724 /* and then to beats */
2725 current_x = region_frames_to_region_beats (current_x);
2727 if (at_front && current_x < canvas_note->note()->end_time()) {
2728 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2730 double len = canvas_note->note()->time() - current_x;
2731 len += canvas_note->note()->length();
2734 /* XXX convert to beats */
2735 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2740 double len = current_x - canvas_note->note()->time();
2743 /* XXX convert to beats */
2744 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2752 _resize_data.clear();
2757 MidiRegionView::abort_resizing ()
2759 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2760 delete (*i)->resize_rect;
2764 _resize_data.clear ();
2768 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2770 uint8_t new_velocity;
2773 new_velocity = event->note()->velocity() + velocity;
2774 clamp_to_0_127(new_velocity);
2776 new_velocity = velocity;
2779 event->set_selected (event->selected()); // change color
2781 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2785 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2790 new_note = event->note()->note() + note;
2795 clamp_to_0_127 (new_note);
2796 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2800 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2802 bool change_start = false;
2803 bool change_length = false;
2804 Evoral::MusicalTime new_start = 0;
2805 Evoral::MusicalTime new_length = 0;
2807 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2809 front_delta: if positive - move the start of the note later in time (shortening it)
2810 if negative - move the start of the note earlier in time (lengthening it)
2812 end_delta: if positive - move the end of the note later in time (lengthening it)
2813 if negative - move the end of the note earlier in time (shortening it)
2817 if (front_delta < 0) {
2819 if (event->note()->time() < -front_delta) {
2822 new_start = event->note()->time() + front_delta; // moves earlier
2825 /* start moved toward zero, so move the end point out to where it used to be.
2826 Note that front_delta is negative, so this increases the length.
2829 new_length = event->note()->length() - front_delta;
2830 change_start = true;
2831 change_length = true;
2835 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2837 if (new_pos < event->note()->end_time()) {
2838 new_start = event->note()->time() + front_delta;
2839 /* start moved toward the end, so move the end point back to where it used to be */
2840 new_length = event->note()->length() - front_delta;
2841 change_start = true;
2842 change_length = true;
2849 bool can_change = true;
2850 if (end_delta < 0) {
2851 if (event->note()->length() < -end_delta) {
2857 new_length = event->note()->length() + end_delta;
2858 change_length = true;
2863 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2866 if (change_length) {
2867 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2872 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2874 uint8_t new_channel;
2878 if (event->note()->channel() < -chn) {
2881 new_channel = event->note()->channel() + chn;
2884 new_channel = event->note()->channel() + chn;
2887 new_channel = (uint8_t) chn;
2890 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2894 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2896 Evoral::MusicalTime new_time;
2900 if (event->note()->time() < -delta) {
2903 new_time = event->note()->time() + delta;
2906 new_time = event->note()->time() + delta;
2912 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2916 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2918 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2922 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2927 if (_selection.empty()) {
2942 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2943 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2949 start_note_diff_command (_("change velocities"));
2951 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2952 Selection::iterator next = i;
2956 if (i == _selection.begin()) {
2957 change_note_velocity (*i, delta, true);
2958 value = (*i)->note()->velocity() + delta;
2960 change_note_velocity (*i, value, false);
2964 change_note_velocity (*i, delta, true);
2973 if (!_selection.empty()) {
2975 snprintf (buf, sizeof (buf), "Vel %d",
2976 (int) (*_selection.begin())->note()->velocity());
2977 show_verbose_cursor (buf, 10, 10);
2983 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2985 if (_selection.empty()) {
3002 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3004 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3008 if ((int8_t) (*i)->note()->note() + delta > 127) {
3015 start_note_diff_command (_("transpose"));
3017 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3018 Selection::iterator next = i;
3020 change_note_note (*i, delta, true);
3028 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3034 /* grab the current grid distance */
3036 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3038 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3039 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3049 start_note_diff_command (_("change note lengths"));
3051 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3052 Selection::iterator next = i;
3055 /* note the negation of the delta for start */
3057 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3066 MidiRegionView::nudge_notes (bool forward)
3068 if (_selection.empty()) {
3072 /* pick a note as the point along the timeline to get the nudge distance.
3073 its not necessarily the earliest note, so we may want to pull the notes out
3074 into a vector and sort before using the first one.
3077 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3079 framecnt_t distance;
3081 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3083 /* grid is off - use nudge distance */
3085 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3091 framepos_t next_pos = ref_point;
3094 if (max_framepos - 1 < next_pos) {
3098 if (next_pos == 0) {
3104 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3105 distance = ref_point - next_pos;
3108 if (distance == 0) {
3112 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
3118 start_note_diff_command (_("nudge"));
3120 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3121 Selection::iterator next = i;
3123 change_note_time (*i, delta, true);
3131 MidiRegionView::change_channel(uint8_t channel)
3133 start_note_diff_command(_("change channel"));
3134 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3135 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3143 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
3145 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3147 pre_enter_cursor = editor->get_canvas_cursor ();
3149 if (_mouse_state == SelectTouchDragging) {
3150 note_selected (ev, true);
3153 show_verbose_cursor (ev->note ());
3157 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
3159 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3161 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3162 (*i)->hide_velocity ();
3165 editor->verbose_cursor()->hide ();
3167 if (pre_enter_cursor) {
3168 editor->set_canvas_cursor (pre_enter_cursor);
3169 pre_enter_cursor = 0;
3174 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* p)
3177 /* XXX should get patch name if we can */
3178 s << _("Bank:") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3179 << _("Program:") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3180 << _("Channel:") << ((int) p->patch()->channel() + 1);
3181 show_verbose_cursor (s.str(), 10, 20);
3186 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
3188 trackview.editor().verbose_cursor()->hide ();
3189 /* focus will transfer back via the enter-notify event sent to this
3195 MidiRegionView::sysex_entered (ArdourCanvas::CanvasSysEx* p)
3199 show_verbose_cursor (s.str(), 10, 20);
3204 MidiRegionView::sysex_left (ArdourCanvas::CanvasSysEx *)
3206 trackview.editor().verbose_cursor()->hide ();
3207 /* focus will transfer back via the enter-notify event sent to this
3213 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3215 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3216 Editing::MouseMode mm = editor->current_mouse_mode();
3217 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3219 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3220 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3221 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3222 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3224 if (pre_enter_cursor && can_set_cursor) {
3225 editor->set_canvas_cursor (pre_enter_cursor);
3231 MidiRegionView::set_frame_color()
3235 TimeAxisViewItem::set_frame_color ();
3242 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3243 } else if (high_enough_for_name) {
3244 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3249 if (!rect_visible) {
3250 f = UINT_RGBA_CHANGE_A (f, 0);
3253 frame->property_fill_color_rgba() = f;
3257 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3259 if (mode == ForceChannel) {
3260 mask = 0xFFFF; // Show all notes as active (below)
3263 // Update notes for selection
3264 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3265 (*i)->on_channel_selection_change(mask);
3268 _last_channel_selection = mask;
3269 _last_channel_mode = mode;
3271 _patch_changes.clear ();
3272 display_patch_changes ();
3276 MidiRegionView::instrument_settings_changed ()
3282 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3284 if (_selection.empty()) {
3288 PublicEditor& editor (trackview.editor());
3292 /* XXX what to do ? */
3296 editor.get_cut_buffer().add (selection_as_cut_buffer());
3304 start_note_diff_command();
3306 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3313 note_diff_remove_note (*i);
3323 MidiRegionView::selection_as_cut_buffer () const
3327 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3328 NoteType* n = (*i)->note().get();
3329 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3332 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3338 /** This method handles undo */
3340 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3346 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3348 trackview.session()->begin_reversible_command (_("paste"));
3350 start_note_diff_command (_("paste"));
3352 Evoral::MusicalTime beat_delta;
3353 Evoral::MusicalTime paste_pos_beats;
3354 Evoral::MusicalTime duration;
3355 Evoral::MusicalTime end_point = 0;
3357 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3358 paste_pos_beats = absolute_frames_to_source_beats (pos);
3359 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3360 paste_pos_beats = 0;
3362 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",
3363 (*mcb.notes().begin())->time(),
3364 (*mcb.notes().rbegin())->end_time(),
3365 duration, pos, _region->position(),
3366 paste_pos_beats, beat_delta));
3370 for (int n = 0; n < (int) times; ++n) {
3372 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3374 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3375 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3377 /* make all newly added notes selected */
3379 note_diff_add_note (copied_note, true);
3380 end_point = copied_note->end_time();
3383 paste_pos_beats += duration;
3386 /* if we pasted past the current end of the region, extend the region */
3388 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3389 framepos_t region_end = _region->position() + _region->length() - 1;
3391 if (end_frame > region_end) {
3393 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3395 _region->clear_changes ();
3396 _region->set_length (end_frame - _region->position());
3397 trackview.session()->add_command (new StatefulDiffCommand (_region));
3402 trackview.session()->commit_reversible_command ();
3405 struct EventNoteTimeEarlyFirstComparator {
3406 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3407 return a->note()->time() < b->note()->time();
3412 MidiRegionView::time_sort_events ()
3414 if (!_sort_needed) {
3418 EventNoteTimeEarlyFirstComparator cmp;
3421 _sort_needed = false;
3425 MidiRegionView::goto_next_note (bool add_to_selection)
3427 bool use_next = false;
3429 if (_events.back()->selected()) {
3433 time_sort_events ();
3435 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3436 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3438 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3439 if ((*i)->selected()) {
3442 } else if (use_next) {
3443 if (channel_mask & (1 << (*i)->note()->channel())) {
3444 if (!add_to_selection) {
3447 note_selected (*i, true, false);
3454 /* use the first one */
3456 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3457 unique_select (_events.front());
3462 MidiRegionView::goto_previous_note (bool add_to_selection)
3464 bool use_next = false;
3466 if (_events.front()->selected()) {
3470 time_sort_events ();
3472 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3473 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3475 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3476 if ((*i)->selected()) {
3479 } else if (use_next) {
3480 if (channel_mask & (1 << (*i)->note()->channel())) {
3481 if (!add_to_selection) {
3484 note_selected (*i, true, false);
3491 /* use the last one */
3493 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3494 unique_select (*(_events.rbegin()));
3499 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3501 bool had_selected = false;
3503 time_sort_events ();
3505 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3506 if ((*i)->selected()) {
3507 selected.insert ((*i)->note());
3508 had_selected = true;
3512 if (allow_all_if_none_selected && !had_selected) {
3513 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3514 selected.insert ((*i)->note());
3520 MidiRegionView::update_ghost_note (double x, double y)
3522 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3527 _note_group->w2i (x, y);
3529 PublicEditor& editor = trackview.editor ();
3531 framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3532 framecnt_t grid_frames;
3533 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3535 /* use region_frames... because we are converting a delta within the region
3539 double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3545 /* note that this sets the time of the ghost note in beats relative to
3546 the start of the source; that is how all note times are stored.
3548 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3549 _ghost_note->note()->set_length (length);
3550 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3551 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3553 /* the ghost note does not appear in ghost regions, so pass false in here */
3554 update_note (_ghost_note, false);
3556 show_verbose_cursor (_ghost_note->note ());
3560 MidiRegionView::create_ghost_note (double x, double y)
3562 remove_ghost_note ();
3564 boost::shared_ptr<NoteType> g (new NoteType);
3565 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3566 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3567 update_ghost_note (x, y);
3568 _ghost_note->show ();
3573 show_verbose_cursor (_ghost_note->note ());
3577 MidiRegionView::snap_changed ()
3583 create_ghost_note (_last_ghost_x, _last_ghost_y);
3587 MidiRegionView::drop_down_keys ()
3589 _mouse_state = None;
3593 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3595 double note = midi_stream_view()->y_to_note(y);
3597 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3599 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3601 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3602 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3603 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3604 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3609 bool add_mrv_selection = false;
3611 if (_selection.empty()) {
3612 add_mrv_selection = true;
3615 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3616 if (_selection.insert (*i).second) {
3617 (*i)->set_selected (true);
3621 if (add_mrv_selection) {
3622 PublicEditor& editor (trackview.editor());
3623 editor.get_selection().add (this);
3628 MidiRegionView::color_handler ()
3630 RegionView::color_handler ();
3632 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3633 (*i)->set_selected ((*i)->selected()); // will change color
3636 /* XXX probably more to do here */
3640 MidiRegionView::enable_display (bool yn)
3642 RegionView::enable_display (yn);
3649 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3651 if (_step_edit_cursor == 0) {
3652 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3654 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3655 _step_edit_cursor->property_y1() = 0;
3656 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3657 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3658 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3661 move_step_edit_cursor (pos);
3662 _step_edit_cursor->show ();
3666 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3668 _step_edit_cursor_position = pos;
3670 if (_step_edit_cursor) {
3671 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3672 _step_edit_cursor->property_x1() = pixel;
3673 set_step_edit_cursor_width (_step_edit_cursor_width);
3678 MidiRegionView::hide_step_edit_cursor ()
3680 if (_step_edit_cursor) {
3681 _step_edit_cursor->hide ();
3686 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3688 _step_edit_cursor_width = beats;
3690 if (_step_edit_cursor) {
3691 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3695 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3696 * @param w Source that the data will end up in.
3699 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3701 if (!_active_notes) {
3702 /* we aren't actively being recorded to */
3706 boost::shared_ptr<MidiSource> src = w.lock ();
3707 if (!src || src != midi_region()->midi_source()) {
3708 /* recorded data was not destined for our source */
3712 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3714 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3716 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3718 framepos_t back = max_framepos;
3720 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3721 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3723 if (ev.is_channel_event()) {
3724 if (_last_channel_mode == FilterChannels) {
3725 if (((uint16_t(1) << ev.channel()) & _last_channel_selection) == 0) {
3731 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3732 frames from the start of the source, and so time_beats is in terms of the
3736 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3738 if (ev.type() == MIDI_CMD_NOTE_ON) {
3740 boost::shared_ptr<NoteType> note (
3741 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3744 add_note (note, true);
3746 /* fix up our note range */
3747 if (ev.note() < _current_range_min) {
3748 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3749 } else if (ev.note() > _current_range_max) {
3750 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3753 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3754 resolve_note (ev.note (), time_beats);
3760 midi_stream_view()->check_record_layers (region(), back);
3764 MidiRegionView::trim_front_starting ()
3766 /* Reparent the note group to the region view's parent, so that it doesn't change
3767 when the region view is trimmed.
3769 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3770 _temporary_note_group->move (group->property_x(), group->property_y());
3771 _note_group->reparent (*_temporary_note_group);
3775 MidiRegionView::trim_front_ending ()
3777 _note_group->reparent (*group);
3778 delete _temporary_note_group;
3779 _temporary_note_group = 0;
3781 if (_region->start() < 0) {
3782 /* Trim drag made start time -ve; fix this */
3783 midi_region()->fix_negative_start ();
3788 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3790 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3792 d.set_position (Gtk::WIN_POS_MOUSE);
3794 int response = d.run();
3797 case Gtk::RESPONSE_ACCEPT:
3799 case Gtk::RESPONSE_REJECT:
3800 delete_patch_change (pc);
3806 change_patch_change (pc->patch(), d.patch ());
3810 MidiRegionView::delete_sysex (CanvasSysEx* sysex)
3812 MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3813 c->remove (sysex->sysex());
3814 _model->apply_command (*trackview.session(), c);
3821 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3823 using namespace MIDI::Name;
3827 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3829 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3831 MIDI::Name::PatchPrimaryKey patch_key;
3832 get_patch_key_at(n->time(), n->channel(), patch_key);
3833 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3835 patch_key.bank_number,
3836 patch_key.program_number,
3842 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3844 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3845 (int) n->channel() + 1,
3846 (int) n->velocity());
3848 show_verbose_cursor(buf, 10, 20);
3852 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3856 trackview.editor().get_pointer_position (wx, wy);
3861 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3863 double x1, y1, x2, y2;
3864 trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3866 if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3867 wy -= (y2 - y1) + 2 * yoffset;
3870 trackview.editor().verbose_cursor()->set (text, wx, wy);
3871 trackview.editor().verbose_cursor()->show ();
3874 /** @param p A session framepos.
3875 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3876 * @return p snapped to the grid subdivision underneath it.
3879 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3881 PublicEditor& editor = trackview.editor ();
3884 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3890 grid_frames = region_beats_to_region_frames (grid_beats);
3892 /* Hack so that we always snap to the note that we are over, instead of snapping
3893 to the next one if we're more than halfway through the one we're over.
3895 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3896 p -= grid_frames / 2;
3899 return snap_frame_to_frame (p);
3902 /** Called when the selection has been cleared in any MidiRegionView.
3903 * @param rv MidiRegionView that the selection was cleared in.
3906 MidiRegionView::selection_cleared (MidiRegionView* rv)
3912 /* Clear our selection in sympathy; but don't signal the fact */
3913 clear_selection (false);
3917 MidiRegionView::note_button_release ()
3919 delete _note_player;