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 "midi++/midnam_patch.h"
32 #include "pbd/memento_command.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "ardour/midi_model.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIEvent.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "canvas/debug.h"
48 #include "canvas/text.h"
50 #include "automation_region_view.h"
51 #include "automation_time_axis.h"
52 #include "control_point.h"
55 #include "editor_drag.h"
56 #include "ghostregion.h"
57 #include "gui_thread.h"
58 #include "item_counts.h"
60 #include "midi_channel_dialog.h"
61 #include "midi_cut_buffer.h"
62 #include "midi_list_editor.h"
63 #include "midi_region_view.h"
64 #include "midi_streamview.h"
65 #include "midi_time_axis.h"
66 #include "midi_util.h"
67 #include "midi_velocity_dialog.h"
68 #include "mouse_cursors.h"
69 #include "note_player.h"
70 #include "paste_context.h"
71 #include "public_editor.h"
72 #include "route_time_axis.h"
73 #include "rgb_macros.h"
74 #include "selection.h"
75 #include "streamview.h"
76 #include "patch_change_dialog.h"
77 #include "verbose_cursor.h"
78 #include "ardour_ui.h"
81 #include "patch_change.h"
86 using namespace ARDOUR;
88 using namespace Editing;
90 using Gtkmm2ext::Keyboard;
92 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
94 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
96 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
97 RouteTimeAxisView& tv,
98 boost::shared_ptr<MidiRegion> r,
100 uint32_t basic_color)
101 : RegionView (parent, tv, r, spu, basic_color)
102 , _current_range_min(0)
103 , _current_range_max(0)
104 , _region_relative_time_converter(r->session().tempo_map(), r->position())
105 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
107 , _note_group (new ArdourCanvas::Container (group))
108 , _note_diff_command (0)
110 , _step_edit_cursor (0)
111 , _step_edit_cursor_width (1.0)
112 , _step_edit_cursor_position (0.0)
113 , _channel_selection_scoped_note (0)
114 , _temporary_note_group (0)
117 , _sort_needed (true)
118 , _optimization_iterator (_events.end())
120 , _no_sound_notes (false)
123 , _grabbed_keyboard (false)
125 , pre_enter_cursor (0)
126 , pre_press_cursor (0)
127 , pre_note_enter_cursor (0)
130 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
131 _note_group->raise_to_top();
132 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
134 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
135 connect_to_diskstream ();
137 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
140 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
141 RouteTimeAxisView& tv,
142 boost::shared_ptr<MidiRegion> r,
144 uint32_t basic_color,
145 TimeAxisViewItem::Visibility visibility)
146 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
147 , _current_range_min(0)
148 , _current_range_max(0)
149 , _region_relative_time_converter(r->session().tempo_map(), r->position())
150 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
152 , _note_group (new ArdourCanvas::Container (parent))
153 , _note_diff_command (0)
155 , _step_edit_cursor (0)
156 , _step_edit_cursor_width (1.0)
157 , _step_edit_cursor_position (0.0)
158 , _channel_selection_scoped_note (0)
159 , _temporary_note_group (0)
162 , _sort_needed (true)
163 , _optimization_iterator (_events.end())
165 , _no_sound_notes (false)
168 , _grabbed_keyboard (false)
170 , pre_enter_cursor (0)
171 , pre_press_cursor (0)
172 , pre_note_enter_cursor (0)
175 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
176 _note_group->raise_to_top();
178 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
180 connect_to_diskstream ();
182 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
186 MidiRegionView::parameter_changed (std::string const & p)
188 if (p == "display-first-midi-bank-as-zero") {
189 if (_enable_display) {
195 MidiRegionView::MidiRegionView (const MidiRegionView& other)
196 : sigc::trackable(other)
198 , _current_range_min(0)
199 , _current_range_max(0)
200 , _region_relative_time_converter(other.region_relative_time_converter())
201 , _source_relative_time_converter(other.source_relative_time_converter())
203 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
204 , _note_diff_command (0)
206 , _step_edit_cursor (0)
207 , _step_edit_cursor_width (1.0)
208 , _step_edit_cursor_position (0.0)
209 , _channel_selection_scoped_note (0)
210 , _temporary_note_group (0)
213 , _sort_needed (true)
214 , _optimization_iterator (_events.end())
216 , _no_sound_notes (false)
219 , _grabbed_keyboard (false)
221 , pre_enter_cursor (0)
222 , pre_press_cursor (0)
223 , pre_note_enter_cursor (0)
229 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
230 : RegionView (other, boost::shared_ptr<Region> (region))
231 , _current_range_min(0)
232 , _current_range_max(0)
233 , _region_relative_time_converter(other.region_relative_time_converter())
234 , _source_relative_time_converter(other.source_relative_time_converter())
236 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
237 , _note_diff_command (0)
239 , _step_edit_cursor (0)
240 , _step_edit_cursor_width (1.0)
241 , _step_edit_cursor_position (0.0)
242 , _channel_selection_scoped_note (0)
243 , _temporary_note_group (0)
246 , _sort_needed (true)
247 , _optimization_iterator (_events.end())
249 , _no_sound_notes (false)
252 , _grabbed_keyboard (false)
254 , pre_enter_cursor (0)
255 , pre_press_cursor (0)
256 , pre_note_enter_cursor (0)
263 MidiRegionView::init (bool wfd)
265 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
267 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
268 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
272 midi_region()->midi_source(0)->load_model();
275 _model = midi_region()->midi_source(0)->model();
276 _enable_display = false;
278 RegionView::init (false);
280 set_height (trackview.current_height());
283 region_sync_changed ();
284 region_resized (ARDOUR::bounds_change);
289 _enable_display = true;
292 display_model (_model);
296 reset_width_dependent_items (_pixel_width);
298 group->raise_to_top();
300 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
301 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
304 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
305 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
307 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
308 boost::bind (&MidiRegionView::snap_changed, this),
311 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
312 connect_to_diskstream ();
314 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
318 MidiRegionView::instrument_info () const
320 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
321 return route_ui->route()->instrument_info();
324 const boost::shared_ptr<ARDOUR::MidiRegion>
325 MidiRegionView::midi_region() const
327 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
331 MidiRegionView::connect_to_diskstream ()
333 midi_view()->midi_track()->DataRecorded.connect(
334 *this, invalidator(*this),
335 boost::bind (&MidiRegionView::data_recorded, this, _1),
340 MidiRegionView::canvas_group_event(GdkEvent* ev)
346 if (!trackview.editor().internal_editing()) {
347 // not in internal edit mode, so just act like a normal region
348 return RegionView::canvas_group_event (ev);
351 const MouseMode m = trackview.editor().current_mouse_mode();
355 case GDK_ENTER_NOTIFY:
356 _last_event_x = ev->crossing.x;
357 _last_event_y = ev->crossing.y;
358 enter_notify(&ev->crossing);
359 // set entered_regionview (among other things)
360 return RegionView::canvas_group_event (ev);
362 case GDK_LEAVE_NOTIFY:
363 _last_event_x = ev->crossing.x;
364 _last_event_y = ev->crossing.y;
365 leave_notify(&ev->crossing);
366 // reset entered_regionview (among other things)
367 return RegionView::canvas_group_event (ev);
369 case GDK_2BUTTON_PRESS:
370 // cannot use double-click to exit internal mode if single-click is being used
371 if ((m != MouseDraw) &&
373 !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier()))) {
374 return trackview.editor().toggle_internal_editing_from_double_click (ev);
379 if (scroll (&ev->scroll)) {
385 return key_press (&ev->key);
387 case GDK_KEY_RELEASE:
388 return key_release (&ev->key);
390 case GDK_BUTTON_PRESS:
391 return button_press (&ev->button);
393 case GDK_BUTTON_RELEASE:
394 r = button_release (&ev->button);
399 case GDK_MOTION_NOTIFY:
400 _last_event_x = ev->motion.x;
401 _last_event_y = ev->motion.y;
402 return motion (&ev->motion);
408 return RegionView::canvas_group_event (ev);
412 MidiRegionView::enter_notify (GdkEventCrossing* ev)
414 trackview.editor().MouseModeChanged.connect (
415 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
425 MidiRegionView::leave_notify (GdkEventCrossing*)
427 _mouse_mode_connection.disconnect ();
436 MidiRegionView::mouse_mode_changed ()
438 if (trackview.editor().internal_editing()) {
439 // Switched in to internal editing mode while entered
442 // Switched out of internal editing mode while entered
448 MidiRegionView::enter_internal()
450 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
451 // Show ghost note under pencil
452 create_ghost_note(_last_event_x, _last_event_y);
455 if (!_selection.empty()) {
456 // Grab keyboard for moving selected notes with arrow keys
457 Keyboard::magic_widget_grab_focus();
458 _grabbed_keyboard = true;
463 MidiRegionView::leave_internal()
465 trackview.editor().verbose_cursor()->hide ();
466 remove_ghost_note ();
468 if (_grabbed_keyboard) {
469 Keyboard::magic_widget_drop_focus();
470 _grabbed_keyboard = false;
475 MidiRegionView::button_press (GdkEventButton* ev)
477 if (ev->button != 1) {
481 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
482 MouseMode m = editor->current_mouse_mode();
484 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
485 pre_press_cursor = editor->get_canvas_cursor ();
486 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
489 if (_mouse_state != SelectTouchDragging) {
491 _pressed_button = ev->button;
492 _mouse_state = Pressed;
497 _pressed_button = ev->button;
503 MidiRegionView::button_release (GdkEventButton* ev)
505 double event_x, event_y;
507 if (ev->button != 1) {
514 group->canvas_to_item (event_x, event_y);
517 PublicEditor& editor = trackview.editor ();
519 if (pre_press_cursor) {
520 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
521 pre_press_cursor = 0;
524 switch (_mouse_state) {
525 case Pressed: // Clicked
527 switch (editor.current_mouse_mode()) {
529 /* no motion occured - simple click */
538 if (Keyboard::is_insert_note_event(ev)) {
540 double event_x, event_y;
544 group->canvas_to_item (event_x, event_y);
546 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
548 /* Shorten the length by 1 tick so that we can add a new note at the next
549 grid snap without it overlapping this one.
551 beats -= Evoral::MusicalTime::tick();
553 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
560 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
562 /* Shorten the length by 1 tick so that we can add a new note at the next
563 grid snap without it overlapping this one.
565 beats -= Evoral::MusicalTime::tick();
567 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
578 case SelectRectDragging:
580 editor.drags()->end_grab ((GdkEvent *) ev);
582 create_ghost_note (ev->x, ev->y);
594 MidiRegionView::motion (GdkEventMotion* ev)
596 PublicEditor& editor = trackview.editor ();
598 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
599 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
600 _mouse_state != AddDragging) {
602 create_ghost_note (ev->x, ev->y);
604 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
605 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
607 update_ghost_note (ev->x, ev->y);
609 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
611 remove_ghost_note ();
612 editor.verbose_cursor()->hide ();
614 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
616 update_ghost_note (ev->x, ev->y);
619 /* any motion immediately hides velocity text that may have been visible */
621 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
622 (*i)->hide_velocity ();
625 switch (_mouse_state) {
628 if (_pressed_button == 1) {
630 MouseMode m = editor.current_mouse_mode();
632 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
633 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
634 _mouse_state = AddDragging;
635 remove_ghost_note ();
636 editor.verbose_cursor()->hide ();
638 } else if (m == MouseObject) {
639 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
640 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
643 _mouse_state = SelectRectDragging;
645 } else if (m == MouseRange) {
646 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
647 _mouse_state = SelectVerticalDragging;
654 case SelectRectDragging:
655 case SelectVerticalDragging:
657 editor.drags()->motion_handler ((GdkEvent *) ev, false);
660 case SelectTouchDragging:
668 /* we may be dragging some non-note object (eg. patch-change, sysex)
671 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
676 MidiRegionView::scroll (GdkEventScroll* ev)
678 if (_selection.empty()) {
682 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
683 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
684 it still works for zoom.
689 trackview.editor().verbose_cursor()->hide ();
691 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
692 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
694 if (ev->direction == GDK_SCROLL_UP) {
695 change_velocities (true, fine, false, together);
696 } else if (ev->direction == GDK_SCROLL_DOWN) {
697 change_velocities (false, fine, false, together);
699 /* left, right: we don't use them */
707 MidiRegionView::key_press (GdkEventKey* ev)
709 /* since GTK bindings are generally activated on press, and since
710 detectable auto-repeat is the name of the game and only sends
711 repeated presses, carry out key actions at key press, not release.
714 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
716 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
717 _mouse_state = SelectTouchDragging;
720 } else if (ev->keyval == GDK_Escape && unmodified) {
724 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
726 bool start = (ev->keyval == GDK_comma);
727 bool end = (ev->keyval == GDK_period);
728 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
729 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
731 change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
735 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
737 if (_selection.empty()) {
744 } else if (ev->keyval == GDK_Tab) {
746 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
747 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
749 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
753 } else if (ev->keyval == GDK_ISO_Left_Tab) {
755 /* Shift-TAB generates ISO Left Tab, for some reason */
757 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
758 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
760 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
766 } else if (ev->keyval == GDK_Up) {
768 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
769 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
770 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
772 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
773 change_velocities (true, fine, allow_smush, together);
775 transpose (true, fine, allow_smush);
779 } else if (ev->keyval == GDK_Down) {
781 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
782 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
783 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
785 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
786 change_velocities (false, fine, allow_smush, together);
788 transpose (false, fine, allow_smush);
792 } else if (ev->keyval == GDK_Left) {
794 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
795 nudge_notes (false, fine);
798 } else if (ev->keyval == GDK_Right) {
800 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
801 nudge_notes (true, fine);
804 } else if (ev->keyval == GDK_c && unmodified) {
808 } else if (ev->keyval == GDK_v && unmodified) {
817 MidiRegionView::key_release (GdkEventKey* ev)
819 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
827 MidiRegionView::channel_edit ()
829 if (_selection.empty()) {
833 /* pick a note somewhat at random (since Selection is a set<>) to
834 * provide the "current" channel for the dialog.
837 uint8_t current_channel = (*_selection.begin())->note()->channel ();
838 MidiChannelDialog channel_dialog (current_channel);
839 int ret = channel_dialog.run ();
842 case Gtk::RESPONSE_OK:
848 uint8_t new_channel = channel_dialog.active_channel ();
850 start_note_diff_command (_("channel edit"));
852 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
853 Selection::iterator next = i;
855 change_note_channel (*i, new_channel);
863 MidiRegionView::velocity_edit ()
865 if (_selection.empty()) {
869 /* pick a note somewhat at random (since Selection is a set<>) to
870 * provide the "current" velocity for the dialog.
873 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
874 MidiVelocityDialog velocity_dialog (current_velocity);
875 int ret = velocity_dialog.run ();
878 case Gtk::RESPONSE_OK:
884 uint8_t new_velocity = velocity_dialog.velocity ();
886 start_note_diff_command (_("velocity edit"));
888 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
889 Selection::iterator next = i;
891 change_note_velocity (*i, new_velocity, false);
899 MidiRegionView::show_list_editor ()
902 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
904 _list_editor->present ();
907 /** Add a note to the model, and the view, at a canvas (click) coordinate.
908 * \param t time in frames relative to the position of the region
909 * \param y vertical position in pixels
910 * \param length duration of the note in beats
911 * \param snap_t true to snap t to the grid, otherwise false.
914 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
916 if (length < 2 * DBL_EPSILON) {
920 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
921 MidiStreamView* const view = mtv->midi_view();
923 const double note = view->y_to_note(y);
925 // Start of note in frames relative to region start
927 framecnt_t grid_frames;
928 t = snap_frame_to_grid_underneath (t, grid_frames);
931 const boost::shared_ptr<NoteType> new_note (
932 new NoteType (mtv->get_channel_for_add (),
933 region_frames_to_region_beats(t + _region->start()),
935 (uint8_t)note, 0x40));
937 if (_model->contains (new_note)) {
941 view->update_note_range(new_note->note());
943 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
945 _model->apply_command(*trackview.session(), cmd);
947 play_midi_note (new_note);
951 MidiRegionView::clear_events (bool with_selection_signal)
953 clear_selection (with_selection_signal);
956 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
957 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
962 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
967 _patch_changes.clear();
969 _optimization_iterator = _events.end();
973 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
977 content_connection.disconnect ();
978 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
982 if (_enable_display) {
988 MidiRegionView::start_note_diff_command (string name)
990 if (!_note_diff_command) {
991 _note_diff_command = _model->new_note_diff_command (name);
996 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
998 if (_note_diff_command) {
999 _note_diff_command->add (note);
1002 _marked_for_selection.insert(note);
1004 if (show_velocity) {
1005 _marked_for_velocity.insert(note);
1010 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1012 if (_note_diff_command && ev->note()) {
1013 _note_diff_command->remove(ev->note());
1018 MidiRegionView::note_diff_add_change (NoteBase* ev,
1019 MidiModel::NoteDiffCommand::Property property,
1022 if (_note_diff_command) {
1023 _note_diff_command->change (ev->note(), property, val);
1028 MidiRegionView::note_diff_add_change (NoteBase* ev,
1029 MidiModel::NoteDiffCommand::Property property,
1030 Evoral::MusicalTime val)
1032 if (_note_diff_command) {
1033 _note_diff_command->change (ev->note(), property, val);
1038 MidiRegionView::apply_diff (bool as_subcommand)
1042 if (!_note_diff_command) {
1046 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1047 // Mark all selected notes for selection when model reloads
1048 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1049 _marked_for_selection.insert((*i)->note());
1053 if (as_subcommand) {
1054 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1056 _model->apply_command (*trackview.session(), _note_diff_command);
1059 _note_diff_command = 0;
1060 midi_view()->midi_track()->playlist_modified();
1062 if (add_or_remove) {
1063 _marked_for_selection.clear();
1066 _marked_for_velocity.clear();
1070 MidiRegionView::abort_command()
1072 delete _note_diff_command;
1073 _note_diff_command = 0;
1078 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1080 if (_optimization_iterator != _events.end()) {
1081 ++_optimization_iterator;
1084 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1085 return *_optimization_iterator;
1088 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1089 if ((*_optimization_iterator)->note() == note) {
1090 return *_optimization_iterator;
1098 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1100 MidiModel::Notes notes;
1101 _model->get_notes (notes, op, val, chan_mask);
1103 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1104 NoteBase* cne = find_canvas_note (*n);
1112 MidiRegionView::redisplay_model()
1114 // Don't redisplay the model if we're currently recording and displaying that
1115 if (_active_notes) {
1123 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1124 (*i)->invalidate ();
1127 MidiModel::ReadLock lock(_model->read_lock());
1129 MidiModel::Notes& notes (_model->notes());
1130 _optimization_iterator = _events.begin();
1132 bool empty_when_starting = _events.empty();
1134 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1136 boost::shared_ptr<NoteType> note (*n);
1140 if (note_in_region_range (note, visible)) {
1142 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1149 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1151 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1163 add_note (note, visible);
1168 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1176 /* remove note items that are no longer valid */
1178 if (!empty_when_starting) {
1179 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1180 if (!(*i)->valid ()) {
1182 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1183 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1185 gr->remove_note (*i);
1190 i = _events.erase (i);
1198 _patch_changes.clear();
1202 display_patch_changes ();
1204 _marked_for_selection.clear ();
1205 _marked_for_velocity.clear ();
1207 /* we may have caused _events to contain things out of order (e.g. if a note
1208 moved earlier or later). we don't generally need them in time order, but
1209 make a note that a sort is required for those cases that require it.
1212 _sort_needed = true;
1216 MidiRegionView::display_patch_changes ()
1218 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1219 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1221 for (uint8_t i = 0; i < 16; ++i) {
1222 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1226 /** @param active_channel true to display patch changes fully, false to display
1227 * them `greyed-out' (as on an inactive channel)
1230 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1232 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1234 if ((*i)->channel() != channel) {
1238 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1239 add_canvas_patch_change (*i, patch_name, active_channel);
1244 MidiRegionView::display_sysexes()
1246 bool have_periodic_system_messages = false;
1247 bool display_periodic_messages = true;
1249 if (!Config->get_never_display_periodic_midi()) {
1251 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1252 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1253 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1256 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1257 have_periodic_system_messages = true;
1263 if (have_periodic_system_messages) {
1264 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1266 /* get an approximate value for the number of samples per video frame */
1268 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1270 /* if we are zoomed out beyond than the cutoff (i.e. more
1271 * frames per pixel than frames per 4 video frames), don't
1272 * show periodic sysex messages.
1275 if (zoom > (video_frame*4)) {
1276 display_periodic_messages = false;
1280 display_periodic_messages = false;
1283 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1285 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1286 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1288 Evoral::MusicalTime time = (*i)->time();
1291 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1292 if (!display_periodic_messages) {
1300 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1301 str << int((*i)->buffer()[b]);
1302 if (b != (*i)->size() -1) {
1306 string text = str.str();
1308 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1310 double height = midi_stream_view()->contents_height();
1312 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1313 // SysEx canvas object!!!
1315 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1316 new SysEx (*this, _note_group, text, height, x, 1.0));
1318 // Show unless message is beyond the region bounds
1319 if (time - _region->start() >= _region->length() || time < _region->start()) {
1325 _sys_exes.push_back(sysex);
1329 MidiRegionView::~MidiRegionView ()
1331 in_destructor = true;
1333 trackview.editor().verbose_cursor()->hide ();
1335 note_delete_connection.disconnect ();
1337 delete _list_editor;
1339 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1341 if (_active_notes) {
1345 _selection_cleared_connection.disconnect ();
1348 clear_events (false);
1351 delete _note_diff_command;
1352 delete _step_edit_cursor;
1353 delete _temporary_note_group;
1357 MidiRegionView::region_resized (const PropertyChange& what_changed)
1359 RegionView::region_resized(what_changed);
1361 if (what_changed.contains (ARDOUR::Properties::position)) {
1362 _region_relative_time_converter.set_origin_b(_region->position());
1363 set_duration(_region->length(), 0);
1364 if (_enable_display) {
1369 if (what_changed.contains (ARDOUR::Properties::start) ||
1370 what_changed.contains (ARDOUR::Properties::position)) {
1371 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1376 MidiRegionView::reset_width_dependent_items (double pixel_width)
1378 RegionView::reset_width_dependent_items(pixel_width);
1380 if (_enable_display) {
1384 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1385 if ((*x)->canvas_item()->width() >= _pixel_width) {
1392 move_step_edit_cursor (_step_edit_cursor_position);
1393 set_step_edit_cursor_width (_step_edit_cursor_width);
1397 MidiRegionView::set_height (double height)
1399 double old_height = _height;
1400 RegionView::set_height(height);
1402 apply_note_range (midi_stream_view()->lowest_note(),
1403 midi_stream_view()->highest_note(),
1404 height != old_height);
1407 name_text->raise_to_top();
1410 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1411 (*x)->set_height (midi_stream_view()->contents_height());
1414 if (_step_edit_cursor) {
1415 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1420 /** Apply the current note range from the stream view
1421 * by repositioning/hiding notes as necessary
1424 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1426 if (!_enable_display) {
1430 if (!force && _current_range_min == min && _current_range_max == max) {
1434 _current_range_min = min;
1435 _current_range_max = max;
1437 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1438 NoteBase* event = *i;
1439 boost::shared_ptr<NoteType> note (event->note());
1441 if (note->note() < _current_range_min ||
1442 note->note() > _current_range_max) {
1448 if (Note* cnote = dynamic_cast<Note*>(event)) {
1450 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1451 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1456 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1463 MidiRegionView::add_ghost (TimeAxisView& tv)
1467 double unit_position = _region->position () / samples_per_pixel;
1468 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1469 MidiGhostRegion* ghost;
1471 if (mtv && mtv->midi_view()) {
1472 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1473 to allow having midi notes on top of note lines and waveforms.
1475 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1477 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1480 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1481 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1482 ghost->add_note(note);
1486 ghost->set_height ();
1487 ghost->set_duration (_region->length() / samples_per_pixel);
1488 ghosts.push_back (ghost);
1490 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1496 /** Begin tracking note state for successive calls to add_event
1499 MidiRegionView::begin_write()
1501 if (_active_notes) {
1502 delete[] _active_notes;
1504 _active_notes = new Note*[128];
1505 for (unsigned i = 0; i < 128; ++i) {
1506 _active_notes[i] = 0;
1511 /** Destroy note state for add_event
1514 MidiRegionView::end_write()
1516 delete[] _active_notes;
1518 _marked_for_selection.clear();
1519 _marked_for_velocity.clear();
1523 /** Resolve an active MIDI note (while recording).
1526 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1528 if (midi_view()->note_mode() != Sustained) {
1532 if (_active_notes && _active_notes[note]) {
1534 /* XXX is end_time really region-centric? I think so, because
1535 this is a new region that we're recording, so source zero is
1536 the same as region zero
1538 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1540 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1541 _active_notes[note]->set_outline_all ();
1542 _active_notes[note] = 0;
1548 /** Extend active notes to rightmost edge of region (if length is changed)
1551 MidiRegionView::extend_active_notes()
1553 if (!_active_notes) {
1557 for (unsigned i=0; i < 128; ++i) {
1558 if (_active_notes[i]) {
1559 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1566 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1568 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1572 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1574 if (!route_ui || !route_ui->midi_track()) {
1578 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1582 /* NotePlayer deletes itself */
1586 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1588 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1592 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1594 if (!route_ui || !route_ui->midi_track()) {
1598 delete _note_player;
1599 _note_player = new NotePlayer (route_ui->midi_track ());
1600 _note_player->add (note);
1601 _note_player->on ();
1605 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1607 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1611 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1613 if (!route_ui || !route_ui->midi_track()) {
1617 delete _note_player;
1618 _note_player = new NotePlayer (route_ui->midi_track());
1620 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1621 _note_player->add (*n);
1624 _note_player->on ();
1629 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1631 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1632 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1634 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1635 (note->note() <= midi_stream_view()->highest_note());
1640 /** Update a canvas note's size from its model note.
1641 * @param ev Canvas note to update.
1642 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1645 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1647 boost::shared_ptr<NoteType> note = ev->note();
1648 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1649 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1654 /* trim note display to not overlap the end of its region */
1656 if (note->length() > 0) {
1657 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1658 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1660 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1663 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1665 if (!note->length()) {
1666 if (_active_notes && note->note() < 128) {
1667 // If this note is already active there's a stuck note,
1668 // finish the old note rectangle
1669 if (_active_notes[note->note()]) {
1670 Note* const old_rect = _active_notes[note->note()];
1671 boost::shared_ptr<NoteType> old_note = old_rect->note();
1672 old_rect->set_x1 (x);
1673 old_rect->set_outline_all ();
1675 _active_notes[note->note()] = ev;
1677 /* outline all but right edge */
1678 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1679 ArdourCanvas::Rectangle::TOP|
1680 ArdourCanvas::Rectangle::LEFT|
1681 ArdourCanvas::Rectangle::BOTTOM));
1683 /* outline all edges */
1684 ev->set_outline_all ();
1687 if (update_ghost_regions) {
1688 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1689 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1691 gr->update_note (ev);
1698 MidiRegionView::update_hit (Hit* ev)
1700 boost::shared_ptr<NoteType> note = ev->note();
1702 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1703 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1704 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1705 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1707 ev->set_position (ArdourCanvas::Duple (x, y));
1708 ev->set_height (diamond_size);
1711 /** Add a MIDI note to the view (with length).
1713 * If in sustained mode, notes with length 0 will be considered active
1714 * notes, and resolve_note should be called when the corresponding note off
1715 * event arrives, to properly display the note.
1718 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1720 NoteBase* event = 0;
1722 if (midi_view()->note_mode() == Sustained) {
1724 Note* ev_rect = new Note (*this, _note_group, note);
1726 update_note (ev_rect);
1730 MidiGhostRegion* gr;
1732 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1733 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1734 gr->add_note(ev_rect);
1738 } else if (midi_view()->note_mode() == Percussive) {
1740 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1742 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1744 update_hit (ev_diamond);
1753 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1754 note_selected(event, true);
1757 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1758 event->show_velocity();
1761 event->on_channel_selection_change (get_selected_channels());
1762 _events.push_back(event);
1771 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1772 MidiStreamView* const view = mtv->midi_view();
1774 view->update_note_range (note->note());
1778 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1779 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1781 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1783 /* potentially extend region to hold new note */
1785 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1786 framepos_t region_end = _region->last_frame();
1788 if (end_frame > region_end) {
1789 _region->set_length (end_frame - _region->position());
1792 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1793 MidiStreamView* const view = mtv->midi_view();
1795 view->update_note_range(new_note->note());
1797 _marked_for_selection.clear ();
1800 start_note_diff_command (_("step add"));
1801 note_diff_add_note (new_note, true, false);
1804 // last_step_edit_note = new_note;
1808 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1810 change_note_lengths (false, false, beats, false, true);
1813 /** Add a new patch change flag to the canvas.
1814 * @param patch the patch change to add
1815 * @param the text to display in the flag
1816 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1819 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1821 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1822 const double x = trackview.editor().sample_to_pixel (region_frames);
1824 double const height = midi_stream_view()->contents_height();
1826 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1827 // so we need to do something more sophisticated to keep its color
1828 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1831 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1832 new PatchChange(*this, group,
1839 if (patch_change->item().width() < _pixel_width) {
1840 // Show unless patch change is beyond the region bounds
1841 if (region_frames < 0 || region_frames >= _region->length()) {
1842 patch_change->hide();
1844 patch_change->show();
1847 patch_change->hide ();
1850 _patch_changes.push_back (patch_change);
1853 MIDI::Name::PatchPrimaryKey
1854 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1856 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1859 /// Return true iff @p pc applies to the given time on the given channel.
1861 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1863 return pc->time() <= time && pc->channel() == channel;
1867 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1869 // The earliest event not before time
1870 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1872 // Go backwards until we find the latest PC for this channel, or the start
1873 while (i != _model->patch_changes().begin() &&
1874 (i == _model->patch_changes().end() ||
1875 !patch_applies(*i, time, channel))) {
1879 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1880 key.bank_number = (*i)->bank();
1881 key.program_number = (*i)->program ();
1883 key.bank_number = key.program_number = 0;
1886 if (!key.is_sane()) {
1887 error << string_compose(_("insane MIDI patch key %1:%2"),
1888 key.bank_number, key.program_number) << endmsg;
1893 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1895 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1897 if (pc.patch()->program() != new_patch.program_number) {
1898 c->change_program (pc.patch (), new_patch.program_number);
1901 int const new_bank = new_patch.bank_number;
1902 if (pc.patch()->bank() != new_bank) {
1903 c->change_bank (pc.patch (), new_bank);
1906 _model->apply_command (*trackview.session(), c);
1908 _patch_changes.clear ();
1909 display_patch_changes ();
1913 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1915 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1917 if (old_change->time() != new_change.time()) {
1918 c->change_time (old_change, new_change.time());
1921 if (old_change->channel() != new_change.channel()) {
1922 c->change_channel (old_change, new_change.channel());
1925 if (old_change->program() != new_change.program()) {
1926 c->change_program (old_change, new_change.program());
1929 if (old_change->bank() != new_change.bank()) {
1930 c->change_bank (old_change, new_change.bank());
1933 _model->apply_command (*trackview.session(), c);
1935 _patch_changes.clear ();
1936 display_patch_changes ();
1939 /** Add a patch change to the region.
1940 * @param t Time in frames relative to region position
1941 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1942 * MidiTimeAxisView::get_channel_for_add())
1945 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1947 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1949 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1950 c->add (MidiModel::PatchChangePtr (
1951 new Evoral::PatchChange<Evoral::MusicalTime> (
1952 absolute_frames_to_source_beats (_region->position() + t),
1953 mtv->get_channel_for_add(), patch.program(), patch.bank()
1958 _model->apply_command (*trackview.session(), c);
1960 _patch_changes.clear ();
1961 display_patch_changes ();
1965 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1967 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1968 c->change_time (pc.patch (), t);
1969 _model->apply_command (*trackview.session(), c);
1971 _patch_changes.clear ();
1972 display_patch_changes ();
1976 MidiRegionView::delete_patch_change (PatchChange* pc)
1978 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1979 c->remove (pc->patch ());
1980 _model->apply_command (*trackview.session(), c);
1982 _patch_changes.clear ();
1983 display_patch_changes ();
1987 MidiRegionView::previous_patch (PatchChange& patch)
1989 if (patch.patch()->program() < 127) {
1990 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1991 key.program_number++;
1992 change_patch_change (patch, key);
1997 MidiRegionView::next_patch (PatchChange& patch)
1999 if (patch.patch()->program() > 0) {
2000 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2001 key.program_number--;
2002 change_patch_change (patch, key);
2007 MidiRegionView::next_bank (PatchChange& patch)
2009 if (patch.patch()->program() < 127) {
2010 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2011 if (key.bank_number > 0) {
2013 change_patch_change (patch, key);
2019 MidiRegionView::previous_bank (PatchChange& patch)
2021 if (patch.patch()->program() > 0) {
2022 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2023 if (key.bank_number < 127) {
2025 change_patch_change (patch, key);
2031 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2033 if (_selection.empty()) {
2037 _selection.erase (cne);
2041 MidiRegionView::delete_selection()
2043 if (_selection.empty()) {
2047 start_note_diff_command (_("delete selection"));
2049 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2050 if ((*i)->selected()) {
2051 _note_diff_command->remove((*i)->note());
2061 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2063 start_note_diff_command (_("delete note"));
2064 _note_diff_command->remove (n);
2067 trackview.editor().verbose_cursor()->hide ();
2071 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2073 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2075 Selection::iterator tmp = i;
2078 (*i)->set_selected (false);
2079 (*i)->hide_velocity ();
2080 _selection.erase (i);
2088 if (!ev && _entered) {
2089 // Clearing selection entirely, ungrab keyboard
2090 Keyboard::magic_widget_drop_focus();
2091 _grabbed_keyboard = false;
2094 /* this does not change the status of this regionview w.r.t the editor
2099 SelectionCleared (this); /* EMIT SIGNAL */
2104 MidiRegionView::unique_select(NoteBase* ev)
2106 const bool selection_was_empty = _selection.empty();
2108 clear_selection_except (ev);
2110 /* don't bother with checking to see if we should remove this
2111 regionview from the editor selection, since we're about to add
2112 another note, and thus put/keep this regionview in the editor
2116 if (!ev->selected()) {
2117 add_to_selection (ev);
2118 if (selection_was_empty && _entered) {
2119 // Grab keyboard for moving notes with arrow keys
2120 Keyboard::magic_widget_grab_focus();
2121 _grabbed_keyboard = true;
2127 MidiRegionView::select_all_notes ()
2131 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2132 add_to_selection (*i);
2137 MidiRegionView::select_range (framepos_t start, framepos_t end)
2141 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2142 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2143 if (t >= start && t <= end) {
2144 add_to_selection (*i);
2150 MidiRegionView::invert_selection ()
2152 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2153 if ((*i)->selected()) {
2154 remove_from_selection(*i);
2156 add_to_selection (*i);
2162 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2164 bool have_selection = !_selection.empty();
2165 uint8_t low_note = 127;
2166 uint8_t high_note = 0;
2167 MidiModel::Notes& notes (_model->notes());
2168 _optimization_iterator = _events.begin();
2170 if (extend && !have_selection) {
2174 /* scan existing selection to get note range */
2176 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2177 if ((*i)->note()->note() < low_note) {
2178 low_note = (*i)->note()->note();
2180 if ((*i)->note()->note() > high_note) {
2181 high_note = (*i)->note()->note();
2188 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2189 /* only note previously selected is the one we are
2190 * reselecting. treat this as cancelling the selection.
2197 low_note = min (low_note, notenum);
2198 high_note = max (high_note, notenum);
2201 _no_sound_notes = true;
2203 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2205 boost::shared_ptr<NoteType> note (*n);
2207 bool select = false;
2209 if (((1 << note->channel()) & channel_mask) != 0) {
2211 if ((note->note() >= low_note && note->note() <= high_note)) {
2214 } else if (note->note() == notenum) {
2220 if ((cne = find_canvas_note (note)) != 0) {
2221 // extend is false because we've taken care of it,
2222 // since it extends by time range, not pitch.
2223 note_selected (cne, add, false);
2227 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2231 _no_sound_notes = false;
2235 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2237 MidiModel::Notes& notes (_model->notes());
2238 _optimization_iterator = _events.begin();
2240 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2242 boost::shared_ptr<NoteType> note (*n);
2245 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2246 if ((cne = find_canvas_note (note)) != 0) {
2247 if (cne->selected()) {
2248 note_deselected (cne);
2250 note_selected (cne, true, false);
2258 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2261 clear_selection_except (ev);
2262 if (!_selection.empty()) {
2263 PublicEditor& editor (trackview.editor());
2264 editor.get_selection().add (this);
2270 if (!ev->selected()) {
2271 add_to_selection (ev);
2275 /* find end of latest note selected, select all between that and the start of "ev" */
2277 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2278 Evoral::MusicalTime latest = Evoral::MusicalTime();
2280 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2281 if ((*i)->note()->end_time() > latest) {
2282 latest = (*i)->note()->end_time();
2284 if ((*i)->note()->time() < earliest) {
2285 earliest = (*i)->note()->time();
2289 if (ev->note()->end_time() > latest) {
2290 latest = ev->note()->end_time();
2293 if (ev->note()->time() < earliest) {
2294 earliest = ev->note()->time();
2297 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2299 /* find notes entirely within OR spanning the earliest..latest range */
2301 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2302 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2303 add_to_selection (*i);
2311 MidiRegionView::note_deselected(NoteBase* ev)
2313 remove_from_selection (ev);
2317 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2319 PublicEditor& editor = trackview.editor();
2321 // Convert to local coordinates
2322 const framepos_t p = _region->position();
2323 const double y = midi_view()->y_position();
2324 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2325 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2326 const double y0 = max(0.0, gy0 - y);
2327 const double y1 = max(0.0, gy1 - y);
2329 // TODO: Make this faster by storing the last updated selection rect, and only
2330 // adjusting things that are in the area that appears/disappeared.
2331 // We probably need a tree to be able to find events in O(log(n)) time.
2333 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2334 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2335 // Rectangles intersect
2336 if (!(*i)->selected()) {
2337 add_to_selection (*i);
2339 } else if ((*i)->selected() && !extend) {
2340 // Rectangles do not intersect
2341 remove_from_selection (*i);
2345 typedef RouteTimeAxisView::AutomationTracks ATracks;
2346 typedef std::list<Selectable*> Selectables;
2348 /* Add control points to selection. */
2349 const ATracks& atracks = midi_view()->automation_tracks();
2350 Selectables selectables;
2351 editor.get_selection().clear_points();
2352 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2353 a->second->get_selectables(start, end, gy0, gy1, selectables);
2354 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2355 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2357 editor.get_selection().add(cp);
2360 a->second->set_selected_points(editor.get_selection().points);
2365 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2371 // TODO: Make this faster by storing the last updated selection rect, and only
2372 // adjusting things that are in the area that appears/disappeared.
2373 // We probably need a tree to be able to find events in O(log(n)) time.
2375 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2376 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2377 // within y- (note-) range
2378 if (!(*i)->selected()) {
2379 add_to_selection (*i);
2381 } else if ((*i)->selected() && !extend) {
2382 remove_from_selection (*i);
2388 MidiRegionView::remove_from_selection (NoteBase* ev)
2390 Selection::iterator i = _selection.find (ev);
2392 if (i != _selection.end()) {
2393 _selection.erase (i);
2394 if (_selection.empty() && _grabbed_keyboard) {
2396 Keyboard::magic_widget_drop_focus();
2397 _grabbed_keyboard = false;
2401 ev->set_selected (false);
2402 ev->hide_velocity ();
2404 if (_selection.empty()) {
2405 PublicEditor& editor (trackview.editor());
2406 editor.get_selection().remove (this);
2411 MidiRegionView::add_to_selection (NoteBase* ev)
2413 const bool selection_was_empty = _selection.empty();
2415 if (_selection.insert (ev).second) {
2416 ev->set_selected (true);
2417 start_playing_midi_note ((ev)->note());
2418 if (selection_was_empty && _entered) {
2419 // Grab keyboard for moving notes with arrow keys
2420 Keyboard::magic_widget_grab_focus();
2421 _grabbed_keyboard = true;
2425 if (selection_was_empty) {
2426 PublicEditor& editor (trackview.editor());
2427 editor.get_selection().add (this);
2432 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2434 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2435 PossibleChord to_play;
2436 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2438 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2439 if ((*i)->note()->time() < earliest) {
2440 earliest = (*i)->note()->time();
2444 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2445 if ((*i)->note()->time() == earliest) {
2446 to_play.push_back ((*i)->note());
2448 (*i)->move_event(dx, dy);
2451 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2453 if (to_play.size() > 1) {
2455 PossibleChord shifted;
2457 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2458 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2459 moved_note->set_note (moved_note->note() + cumulative_dy);
2460 shifted.push_back (moved_note);
2463 start_playing_midi_chord (shifted);
2465 } else if (!to_play.empty()) {
2467 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2468 moved_note->set_note (moved_note->note() + cumulative_dy);
2469 start_playing_midi_note (moved_note);
2475 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2477 uint8_t lowest_note_in_selection = 127;
2478 uint8_t highest_note_in_selection = 0;
2479 uint8_t highest_note_difference = 0;
2481 // find highest and lowest notes first
2483 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2484 uint8_t pitch = (*i)->note()->note();
2485 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2486 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2490 cerr << "dnote: " << (int) dnote << endl;
2491 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2492 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2493 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2494 << int(highest_note_in_selection) << endl;
2495 cerr << "selection size: " << _selection.size() << endl;
2496 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2499 // Make sure the note pitch does not exceed the MIDI standard range
2500 if (highest_note_in_selection + dnote > 127) {
2501 highest_note_difference = highest_note_in_selection - 127;
2504 start_note_diff_command (_("move notes"));
2506 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2508 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2509 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2515 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2517 uint8_t original_pitch = (*i)->note()->note();
2518 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2520 // keep notes in standard midi range
2521 clamp_to_0_127(new_pitch);
2523 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2524 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2526 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2531 // care about notes being moved beyond the upper/lower bounds on the canvas
2532 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2533 highest_note_in_selection > midi_stream_view()->highest_note()) {
2534 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2538 /** @param x Pixel relative to the region position.
2539 * @return Snapped frame relative to the region position.
2542 MidiRegionView::snap_pixel_to_sample(double x)
2544 PublicEditor& editor (trackview.editor());
2545 return snap_frame_to_frame (editor.pixel_to_sample (x));
2548 /** @param x Pixel relative to the region position.
2549 * @return Snapped pixel relative to the region position.
2552 MidiRegionView::snap_to_pixel(double x)
2554 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2558 MidiRegionView::get_position_pixels()
2560 framepos_t region_frame = get_position();
2561 return trackview.editor().sample_to_pixel(region_frame);
2565 MidiRegionView::get_end_position_pixels()
2567 framepos_t frame = get_position() + get_duration ();
2568 return trackview.editor().sample_to_pixel(frame);
2572 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2574 /* the time converter will return the frame corresponding to `beats'
2575 relative to the start of the source. The start of the source
2576 is an implied position given by region->position - region->start
2578 const framepos_t source_start = _region->position() - _region->start();
2579 return source_start + _source_relative_time_converter.to (beats);
2583 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2585 /* the `frames' argument needs to be converted into a frame count
2586 relative to the start of the source before being passed in to the
2589 const framepos_t source_start = _region->position() - _region->start();
2590 return _source_relative_time_converter.from (frames - source_start);
2594 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2596 return _region_relative_time_converter.to(beats);
2600 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2602 return _region_relative_time_converter.from(frames);
2606 MidiRegionView::begin_resizing (bool /*at_front*/)
2608 _resize_data.clear();
2610 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2611 Note *note = dynamic_cast<Note*> (*i);
2613 // only insert CanvasNotes into the map
2615 NoteResizeData *resize_data = new NoteResizeData();
2616 resize_data->note = note;
2618 // create a new SimpleRect from the note which will be the resize preview
2619 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2620 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2622 // calculate the colors: get the color settings
2623 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2624 ARDOUR_UI::config()->color ("midi note selected"),
2627 // make the resize preview notes more transparent and bright
2628 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2630 // calculate color based on note velocity
2631 resize_rect->set_fill_color (UINT_INTERPOLATE(
2632 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2636 resize_rect->set_outline_color (NoteBase::calculate_outline (
2637 ARDOUR_UI::config()->color ("midi note selected")));
2639 resize_data->resize_rect = resize_rect;
2640 _resize_data.push_back(resize_data);
2645 /** Update resizing notes while user drags.
2646 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2647 * @param at_front which end of the note (true == note on, false == note off)
2648 * @param delta_x change in mouse position since the start of the drag
2649 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2650 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2651 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2652 * as the \a primary note.
2655 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2657 bool cursor_set = false;
2659 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2660 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2661 Note* canvas_note = (*i)->note;
2666 current_x = canvas_note->x0() + delta_x;
2668 current_x = primary->x0() + delta_x;
2672 current_x = canvas_note->x1() + delta_x;
2674 current_x = primary->x1() + delta_x;
2678 if (current_x < 0) {
2679 // This works even with snapping because RegionView::snap_frame_to_frame()
2680 // snaps forward if the snapped sample is before the beginning of the region
2683 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2684 current_x = trackview.editor().sample_to_pixel(_region->length());
2688 resize_rect->set_x0 (snap_to_pixel(current_x));
2689 resize_rect->set_x1 (canvas_note->x1());
2691 resize_rect->set_x1 (snap_to_pixel(current_x));
2692 resize_rect->set_x0 (canvas_note->x0());
2696 const double snapped_x = snap_pixel_to_sample (current_x);
2697 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2698 Evoral::MusicalTime len = Evoral::MusicalTime();
2701 if (beats < canvas_note->note()->end_time()) {
2702 len = canvas_note->note()->time() - beats;
2703 len += canvas_note->note()->length();
2706 if (beats >= canvas_note->note()->time()) {
2707 len = beats - canvas_note->note()->time();
2712 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2713 show_verbose_cursor (buf, 0, 0);
2722 /** Finish resizing notes when the user releases the mouse button.
2723 * Parameters the same as for \a update_resizing().
2726 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2728 start_note_diff_command (_("resize notes"));
2730 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2731 Note* canvas_note = (*i)->note;
2732 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2734 /* Get the new x position for this resize, which is in pixels relative
2735 * to the region position.
2742 current_x = canvas_note->x0() + delta_x;
2744 current_x = primary->x0() + delta_x;
2748 current_x = canvas_note->x1() + delta_x;
2750 current_x = primary->x1() + delta_x;
2754 if (current_x < 0) {
2757 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2758 current_x = trackview.editor().sample_to_pixel(_region->length());
2761 /* Convert that to a frame within the source */
2762 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2764 /* and then to beats */
2765 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2767 if (at_front && x_beats < canvas_note->note()->end_time()) {
2768 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2770 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2771 len += canvas_note->note()->length();
2774 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2779 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2782 /* XXX convert to beats */
2783 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2791 _resize_data.clear();
2796 MidiRegionView::abort_resizing ()
2798 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2799 delete (*i)->resize_rect;
2803 _resize_data.clear ();
2807 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2809 uint8_t new_velocity;
2812 new_velocity = event->note()->velocity() + velocity;
2813 clamp_to_0_127(new_velocity);
2815 new_velocity = velocity;
2818 event->set_selected (event->selected()); // change color
2820 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2824 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2829 new_note = event->note()->note() + note;
2834 clamp_to_0_127 (new_note);
2835 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2839 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2841 bool change_start = false;
2842 bool change_length = false;
2843 Evoral::MusicalTime new_start;
2844 Evoral::MusicalTime new_length;
2846 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2848 front_delta: if positive - move the start of the note later in time (shortening it)
2849 if negative - move the start of the note earlier in time (lengthening it)
2851 end_delta: if positive - move the end of the note later in time (lengthening it)
2852 if negative - move the end of the note earlier in time (shortening it)
2855 if (!!front_delta) {
2856 if (front_delta < 0) {
2858 if (event->note()->time() < -front_delta) {
2859 new_start = Evoral::MusicalTime();
2861 new_start = event->note()->time() + front_delta; // moves earlier
2864 /* start moved toward zero, so move the end point out to where it used to be.
2865 Note that front_delta is negative, so this increases the length.
2868 new_length = event->note()->length() - front_delta;
2869 change_start = true;
2870 change_length = true;
2874 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2876 if (new_pos < event->note()->end_time()) {
2877 new_start = event->note()->time() + front_delta;
2878 /* start moved toward the end, so move the end point back to where it used to be */
2879 new_length = event->note()->length() - front_delta;
2880 change_start = true;
2881 change_length = true;
2888 bool can_change = true;
2889 if (end_delta < 0) {
2890 if (event->note()->length() < -end_delta) {
2896 new_length = event->note()->length() + end_delta;
2897 change_length = true;
2902 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2905 if (change_length) {
2906 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2911 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2913 uint8_t new_channel;
2917 if (event->note()->channel() < -chn) {
2920 new_channel = event->note()->channel() + chn;
2923 new_channel = event->note()->channel() + chn;
2926 new_channel = (uint8_t) chn;
2929 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2933 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2935 Evoral::MusicalTime new_time;
2939 if (event->note()->time() < -delta) {
2940 new_time = Evoral::MusicalTime();
2942 new_time = event->note()->time() + delta;
2945 new_time = event->note()->time() + delta;
2951 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2955 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2957 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2961 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2966 if (_selection.empty()) {
2981 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2982 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2988 start_note_diff_command (_("change velocities"));
2990 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2991 Selection::iterator next = i;
2995 if (i == _selection.begin()) {
2996 change_note_velocity (*i, delta, true);
2997 value = (*i)->note()->velocity() + delta;
2999 change_note_velocity (*i, value, false);
3003 change_note_velocity (*i, delta, true);
3012 if (!_selection.empty()) {
3014 snprintf (buf, sizeof (buf), "Vel %d",
3015 (int) (*_selection.begin())->note()->velocity());
3016 show_verbose_cursor (buf, 10, 10);
3022 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3024 if (_selection.empty()) {
3041 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3043 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3047 if ((int8_t) (*i)->note()->note() + delta > 127) {
3054 start_note_diff_command (_("transpose"));
3056 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3057 Selection::iterator next = i;
3059 change_note_note (*i, delta, true);
3067 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3071 delta = Evoral::MusicalTime(1.0/128.0);
3073 /* grab the current grid distance */
3074 delta = get_grid_beats(_region->position());
3082 start_note_diff_command (_("change note lengths"));
3084 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3085 Selection::iterator next = i;
3088 /* note the negation of the delta for start */
3091 (start ? -delta : Evoral::MusicalTime()),
3092 (end ? delta : Evoral::MusicalTime()));
3101 MidiRegionView::nudge_notes (bool forward, bool fine)
3103 if (_selection.empty()) {
3107 /* pick a note as the point along the timeline to get the nudge distance.
3108 its not necessarily the earliest note, so we may want to pull the notes out
3109 into a vector and sort before using the first one.
3112 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3113 Evoral::MusicalTime delta;
3117 /* non-fine, move by 1 bar regardless of snap */
3118 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3120 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3122 /* grid is off - use nudge distance */
3125 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3126 delta = region_frames_to_region_beats (fabs ((double)distance));
3132 framepos_t next_pos = ref_point;
3135 if (max_framepos - 1 < next_pos) {
3139 if (next_pos == 0) {
3145 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3146 const framecnt_t distance = ref_point - next_pos;
3147 delta = region_frames_to_region_beats (fabs ((double)distance));
3158 start_note_diff_command (_("nudge"));
3160 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3161 Selection::iterator next = i;
3163 change_note_time (*i, delta, true);
3171 MidiRegionView::change_channel(uint8_t channel)
3173 start_note_diff_command(_("change channel"));
3174 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3175 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3183 MidiRegionView::note_entered(NoteBase* ev)
3185 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3187 pre_note_enter_cursor = editor->get_canvas_cursor ();
3189 if (_mouse_state == SelectTouchDragging) {
3190 note_selected (ev, true);
3193 show_verbose_cursor (ev->note ());
3197 MidiRegionView::note_left (NoteBase*)
3199 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3201 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3202 (*i)->hide_velocity ();
3205 editor->verbose_cursor()->hide ();
3207 if (pre_note_enter_cursor) {
3208 editor->set_canvas_cursor (pre_note_enter_cursor);
3209 pre_note_enter_cursor = 0;
3214 MidiRegionView::patch_entered (PatchChange* p)
3217 /* XXX should get patch name if we can */
3218 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3219 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3220 << _("Channel ") << ((int) p->patch()->channel() + 1);
3221 show_verbose_cursor (s.str(), 10, 20);
3222 p->item().grab_focus();
3226 MidiRegionView::patch_left (PatchChange *)
3228 trackview.editor().verbose_cursor()->hide ();
3229 /* focus will transfer back via the enter-notify event sent to this
3235 MidiRegionView::sysex_entered (SysEx* p)
3239 // need a way to extract text from p->_flag->_text
3241 // show_verbose_cursor (s.str(), 10, 20);
3242 p->item().grab_focus();
3246 MidiRegionView::sysex_left (SysEx *)
3248 trackview.editor().verbose_cursor()->hide ();
3249 /* focus will transfer back via the enter-notify event sent to this
3255 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3257 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3258 Editing::MouseMode mm = editor->current_mouse_mode();
3259 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3261 if (can_set_cursor) {
3262 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3263 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3264 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3265 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3266 } else if (pre_note_enter_cursor) {
3267 editor->set_canvas_cursor (pre_note_enter_cursor);
3273 MidiRegionView::set_frame_color()
3277 TimeAxisViewItem::set_frame_color ();
3284 f = ARDOUR_UI::config()->color ("selected region base");
3285 } else if (high_enough_for_name) {
3286 f= ARDOUR_UI::config()->color ("midi frame base");
3291 if (!rect_visible) {
3292 f = UINT_RGBA_CHANGE_A (f, 80);
3295 frame->set_fill_color (f);
3299 MidiRegionView::midi_channel_mode_changed ()
3301 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3302 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3303 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3305 if (mode == ForceChannel) {
3306 mask = 0xFFFF; // Show all notes as active (below)
3309 // Update notes for selection
3310 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3311 (*i)->on_channel_selection_change (mask);
3314 _patch_changes.clear ();
3315 display_patch_changes ();
3319 MidiRegionView::instrument_settings_changed ()
3325 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3327 if (_selection.empty()) {
3331 PublicEditor& editor (trackview.editor());
3335 /* XXX what to do ? */
3339 editor.get_cut_buffer().add (selection_as_cut_buffer());
3347 start_note_diff_command();
3349 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3356 note_diff_remove_note (*i);
3366 MidiRegionView::selection_as_cut_buffer () const
3370 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3371 NoteType* n = (*i)->note().get();
3372 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3375 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3381 /** This method handles undo */
3383 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3385 trackview.session()->begin_reversible_command (Operations::paste);
3387 // Paste notes, if available
3388 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3389 if (m != selection.midi_notes.end()) {
3390 ctx.counts.increase_n_notes();
3391 paste_internal(pos, ctx.count, ctx.times, **m);
3394 // Paste control points to automation children, if available
3395 typedef RouteTimeAxisView::AutomationTracks ATracks;
3396 const ATracks& atracks = midi_view()->automation_tracks();
3397 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3398 a->second->paste(pos, selection, ctx);
3401 trackview.session()->commit_reversible_command ();
3406 /** This method handles undo */
3408 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3414 start_note_diff_command (_("paste"));
3416 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3417 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3418 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3419 const Evoral::MusicalTime duration = last_time - first_time;
3420 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3421 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3422 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3423 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3425 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3428 duration, pos, _region->position(),
3433 for (int n = 0; n < (int) times; ++n) {
3435 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3437 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3438 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3440 /* make all newly added notes selected */
3442 note_diff_add_note (copied_note, true);
3443 end_point = copied_note->end_time();
3447 /* if we pasted past the current end of the region, extend the region */
3449 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3450 framepos_t region_end = _region->position() + _region->length() - 1;
3452 if (end_frame > region_end) {
3454 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3456 _region->clear_changes ();
3457 _region->set_length (end_frame - _region->position());
3458 trackview.session()->add_command (new StatefulDiffCommand (_region));
3464 struct EventNoteTimeEarlyFirstComparator {
3465 bool operator() (NoteBase* a, NoteBase* b) {
3466 return a->note()->time() < b->note()->time();
3471 MidiRegionView::time_sort_events ()
3473 if (!_sort_needed) {
3477 EventNoteTimeEarlyFirstComparator cmp;
3480 _sort_needed = false;
3484 MidiRegionView::goto_next_note (bool add_to_selection)
3486 bool use_next = false;
3488 if (_events.back()->selected()) {
3492 time_sort_events ();
3494 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3495 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3497 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3498 if ((*i)->selected()) {
3501 } else if (use_next) {
3502 if (channel_mask & (1 << (*i)->note()->channel())) {
3503 if (!add_to_selection) {
3506 note_selected (*i, true, false);
3513 /* use the first one */
3515 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3516 unique_select (_events.front());
3521 MidiRegionView::goto_previous_note (bool add_to_selection)
3523 bool use_next = false;
3525 if (_events.front()->selected()) {
3529 time_sort_events ();
3531 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3532 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3534 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3535 if ((*i)->selected()) {
3538 } else if (use_next) {
3539 if (channel_mask & (1 << (*i)->note()->channel())) {
3540 if (!add_to_selection) {
3543 note_selected (*i, true, false);
3550 /* use the last one */
3552 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3553 unique_select (*(_events.rbegin()));
3558 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3560 bool had_selected = false;
3562 time_sort_events ();
3564 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3565 if ((*i)->selected()) {
3566 selected.insert ((*i)->note());
3567 had_selected = true;
3571 if (allow_all_if_none_selected && !had_selected) {
3572 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3573 selected.insert ((*i)->note());
3579 MidiRegionView::update_ghost_note (double x, double y)
3581 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3586 _note_group->canvas_to_item (x, y);
3588 PublicEditor& editor = trackview.editor ();
3590 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3591 framecnt_t grid_frames;
3592 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3594 /* use region_frames... because we are converting a delta within the region
3597 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3599 /* note that this sets the time of the ghost note in beats relative to
3600 the start of the source; that is how all note times are stored.
3602 _ghost_note->note()->set_time (
3603 std::max(Evoral::MusicalTime(),
3604 absolute_frames_to_source_beats (f + _region->position ())));
3605 _ghost_note->note()->set_length (length);
3606 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3607 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3609 /* the ghost note does not appear in ghost regions, so pass false in here */
3610 update_note (_ghost_note, false);
3612 show_verbose_cursor (_ghost_note->note ());
3616 MidiRegionView::create_ghost_note (double x, double y)
3618 remove_ghost_note ();
3620 boost::shared_ptr<NoteType> g (new NoteType);
3621 _ghost_note = new Note (*this, _note_group, g);
3622 _ghost_note->set_ignore_events (true);
3623 _ghost_note->set_outline_color (0x000000aa);
3624 if (x < 0) { x = 0; }
3625 update_ghost_note (x, y);
3626 _ghost_note->show ();
3631 show_verbose_cursor (_ghost_note->note ());
3635 MidiRegionView::remove_ghost_note ()
3642 MidiRegionView::snap_changed ()
3648 create_ghost_note (_last_ghost_x, _last_ghost_y);
3652 MidiRegionView::drop_down_keys ()
3654 _mouse_state = None;
3658 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3660 /* XXX: This is dead code. What was it for? */
3662 double note = midi_stream_view()->y_to_note(y);
3664 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3666 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3668 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3669 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3670 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3671 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3676 bool add_mrv_selection = false;
3678 if (_selection.empty()) {
3679 add_mrv_selection = true;
3682 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3683 if (_selection.insert (*i).second) {
3684 (*i)->set_selected (true);
3688 if (add_mrv_selection) {
3689 PublicEditor& editor (trackview.editor());
3690 editor.get_selection().add (this);
3695 MidiRegionView::color_handler ()
3697 RegionView::color_handler ();
3699 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3700 (*i)->set_selected ((*i)->selected()); // will change color
3703 /* XXX probably more to do here */
3707 MidiRegionView::enable_display (bool yn)
3709 RegionView::enable_display (yn);
3716 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3718 if (_step_edit_cursor == 0) {
3719 ArdourCanvas::Item* const group = get_canvas_group();
3721 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3722 _step_edit_cursor->set_y0 (0);
3723 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3724 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3725 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3728 move_step_edit_cursor (pos);
3729 _step_edit_cursor->show ();
3733 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3735 _step_edit_cursor_position = pos;
3737 if (_step_edit_cursor) {
3738 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3739 _step_edit_cursor->set_x0 (pixel);
3740 set_step_edit_cursor_width (_step_edit_cursor_width);
3745 MidiRegionView::hide_step_edit_cursor ()
3747 if (_step_edit_cursor) {
3748 _step_edit_cursor->hide ();
3753 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3755 _step_edit_cursor_width = beats;
3757 if (_step_edit_cursor) {
3758 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3762 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3763 * @param w Source that the data will end up in.
3766 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3768 if (!_active_notes) {
3769 /* we aren't actively being recorded to */
3773 boost::shared_ptr<MidiSource> src = w.lock ();
3774 if (!src || src != midi_region()->midi_source()) {
3775 /* recorded data was not destined for our source */
3779 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3781 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3783 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3785 framepos_t back = max_framepos;
3787 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3788 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3790 if (ev.is_channel_event()) {
3791 if (get_channel_mode() == FilterChannels) {
3792 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3798 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3799 frames from the start of the source, and so time_beats is in terms of the
3803 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3805 if (ev.type() == MIDI_CMD_NOTE_ON) {
3806 boost::shared_ptr<NoteType> note (
3807 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3809 add_note (note, true);
3811 /* fix up our note range */
3812 if (ev.note() < _current_range_min) {
3813 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3814 } else if (ev.note() > _current_range_max) {
3815 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3818 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3819 resolve_note (ev.note (), time_beats);
3825 midi_stream_view()->check_record_layers (region(), back);
3829 MidiRegionView::trim_front_starting ()
3831 /* Reparent the note group to the region view's parent, so that it doesn't change
3832 when the region view is trimmed.
3834 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3835 _temporary_note_group->move (group->position ());
3836 _note_group->reparent (_temporary_note_group);
3840 MidiRegionView::trim_front_ending ()
3842 _note_group->reparent (group);
3843 delete _temporary_note_group;
3844 _temporary_note_group = 0;
3846 if (_region->start() < 0) {
3847 /* Trim drag made start time -ve; fix this */
3848 midi_region()->fix_negative_start ();
3853 MidiRegionView::edit_patch_change (PatchChange* pc)
3855 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3857 int response = d.run();
3860 case Gtk::RESPONSE_ACCEPT:
3862 case Gtk::RESPONSE_REJECT:
3863 delete_patch_change (pc);
3869 change_patch_change (pc->patch(), d.patch ());
3873 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3876 // sysyex object doesn't have a pointer to a sysex event
3877 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3878 // c->remove (sysex->sysex());
3879 // _model->apply_command (*trackview.session(), c);
3881 //_sys_exes.clear ();
3882 // display_sysexes();
3886 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3888 using namespace MIDI::Name;
3892 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3894 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3896 MIDI::Name::PatchPrimaryKey patch_key;
3897 get_patch_key_at(n->time(), n->channel(), patch_key);
3898 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3900 patch_key.bank_number,
3901 patch_key.program_number,
3907 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3909 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3910 (int) n->channel() + 1,
3911 (int) n->velocity());
3913 show_verbose_cursor(buf, 10, 20);
3917 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3919 trackview.editor().verbose_cursor()->set (text);
3920 trackview.editor().verbose_cursor()->show ();
3921 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3924 /** @param p A session framepos.
3925 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3926 * @return p snapped to the grid subdivision underneath it.
3929 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3931 PublicEditor& editor = trackview.editor ();
3933 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3935 grid_frames = region_beats_to_region_frames (grid_beats);
3937 /* Hack so that we always snap to the note that we are over, instead of snapping
3938 to the next one if we're more than halfway through the one we're over.
3940 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3941 p -= grid_frames / 2;
3944 return snap_frame_to_frame (p);
3947 /** Called when the selection has been cleared in any MidiRegionView.
3948 * @param rv MidiRegionView that the selection was cleared in.
3951 MidiRegionView::selection_cleared (MidiRegionView* rv)
3957 /* Clear our selection in sympathy; but don't signal the fact */
3958 clear_selection (false);
3962 MidiRegionView::note_button_release ()
3964 delete _note_player;
3969 MidiRegionView::get_channel_mode () const
3971 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3972 return rtav->midi_track()->get_playback_channel_mode();
3976 MidiRegionView::get_selected_channels () const
3978 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3979 return rtav->midi_track()->get_playback_channel_mask();
3984 MidiRegionView::get_grid_beats(framepos_t pos) const
3986 PublicEditor& editor = trackview.editor();
3987 bool success = false;
3988 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3990 beats = Evoral::MusicalTime(1);