2 Copyright (C) 2001-2011 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "gtkmm2ext/gtk_ui.h"
28 #include <sigc++/signal.h>
30 #include "pbd/memento_command.h"
31 #include "pbd/stateful_diff_command.h"
33 #include "ardour/midi_model.h"
34 #include "ardour/midi_patch_manager.h"
35 #include "ardour/midi_region.h"
36 #include "ardour/midi_source.h"
37 #include "ardour/midi_track.h"
38 #include "ardour/operations.h"
39 #include "ardour/session.h"
41 #include "evoral/Parameter.hpp"
42 #include "evoral/MIDIEvent.hpp"
43 #include "evoral/Control.hpp"
44 #include "evoral/midi_util.h"
46 #include "canvas/debug.h"
47 #include "canvas/text.h"
49 #include "automation_region_view.h"
50 #include "automation_time_axis.h"
51 #include "control_point.h"
54 #include "editor_drag.h"
55 #include "ghostregion.h"
56 #include "gui_thread.h"
57 #include "item_counts.h"
59 #include "midi_channel_dialog.h"
60 #include "midi_cut_buffer.h"
61 #include "midi_list_editor.h"
62 #include "midi_region_view.h"
63 #include "midi_streamview.h"
64 #include "midi_time_axis.h"
65 #include "midi_util.h"
66 #include "midi_velocity_dialog.h"
67 #include "mouse_cursors.h"
68 #include "note_player.h"
69 #include "paste_context.h"
70 #include "public_editor.h"
71 #include "route_time_axis.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "streamview.h"
75 #include "patch_change_dialog.h"
76 #include "verbose_cursor.h"
77 #include "ardour_ui.h"
80 #include "patch_change.h"
85 using namespace ARDOUR;
87 using namespace Editing;
89 using Gtkmm2ext::Keyboard;
91 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
93 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
95 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
96 RouteTimeAxisView& tv,
97 boost::shared_ptr<MidiRegion> r,
100 : RegionView (parent, tv, r, spu, basic_color)
101 , _current_range_min(0)
102 , _current_range_max(0)
103 , _region_relative_time_converter(r->session().tempo_map(), r->position())
104 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
106 , _note_group (new ArdourCanvas::Container (group))
107 , _note_diff_command (0)
109 , _step_edit_cursor (0)
110 , _step_edit_cursor_width (1.0)
111 , _step_edit_cursor_position (0.0)
112 , _channel_selection_scoped_note (0)
113 , _temporary_note_group (0)
116 , _sort_needed (true)
117 , _optimization_iterator (_events.end())
119 , _no_sound_notes (false)
122 , _grabbed_keyboard (false)
124 , pre_enter_cursor (0)
125 , pre_press_cursor (0)
126 , pre_note_enter_cursor (0)
129 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
130 _note_group->raise_to_top();
131 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
133 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
134 connect_to_diskstream ();
136 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
139 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
140 RouteTimeAxisView& tv,
141 boost::shared_ptr<MidiRegion> r,
143 uint32_t basic_color,
144 TimeAxisViewItem::Visibility visibility)
145 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
146 , _current_range_min(0)
147 , _current_range_max(0)
148 , _region_relative_time_converter(r->session().tempo_map(), r->position())
149 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
151 , _note_group (new ArdourCanvas::Container (parent))
152 , _note_diff_command (0)
154 , _step_edit_cursor (0)
155 , _step_edit_cursor_width (1.0)
156 , _step_edit_cursor_position (0.0)
157 , _channel_selection_scoped_note (0)
158 , _temporary_note_group (0)
161 , _sort_needed (true)
162 , _optimization_iterator (_events.end())
164 , _no_sound_notes (false)
167 , _grabbed_keyboard (false)
169 , pre_enter_cursor (0)
170 , pre_press_cursor (0)
171 , pre_note_enter_cursor (0)
174 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
175 _note_group->raise_to_top();
177 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
179 connect_to_diskstream ();
181 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
185 MidiRegionView::parameter_changed (std::string const & p)
187 if (p == "display-first-midi-bank-as-zero") {
188 if (_enable_display) {
194 MidiRegionView::MidiRegionView (const MidiRegionView& other)
195 : sigc::trackable(other)
197 , _current_range_min(0)
198 , _current_range_max(0)
199 , _region_relative_time_converter(other.region_relative_time_converter())
200 , _source_relative_time_converter(other.source_relative_time_converter())
202 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
203 , _note_diff_command (0)
205 , _step_edit_cursor (0)
206 , _step_edit_cursor_width (1.0)
207 , _step_edit_cursor_position (0.0)
208 , _channel_selection_scoped_note (0)
209 , _temporary_note_group (0)
212 , _sort_needed (true)
213 , _optimization_iterator (_events.end())
215 , _no_sound_notes (false)
218 , _grabbed_keyboard (false)
220 , pre_enter_cursor (0)
221 , pre_press_cursor (0)
222 , pre_note_enter_cursor (0)
228 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
229 : RegionView (other, boost::shared_ptr<Region> (region))
230 , _current_range_min(0)
231 , _current_range_max(0)
232 , _region_relative_time_converter(other.region_relative_time_converter())
233 , _source_relative_time_converter(other.source_relative_time_converter())
235 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
236 , _note_diff_command (0)
238 , _step_edit_cursor (0)
239 , _step_edit_cursor_width (1.0)
240 , _step_edit_cursor_position (0.0)
241 , _channel_selection_scoped_note (0)
242 , _temporary_note_group (0)
245 , _sort_needed (true)
246 , _optimization_iterator (_events.end())
248 , _no_sound_notes (false)
251 , _grabbed_keyboard (false)
253 , pre_enter_cursor (0)
254 , pre_press_cursor (0)
255 , pre_note_enter_cursor (0)
262 MidiRegionView::init (bool wfd)
264 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
266 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
267 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
271 midi_region()->midi_source(0)->load_model();
274 _model = midi_region()->midi_source(0)->model();
275 _enable_display = false;
277 RegionView::init (false);
279 set_height (trackview.current_height());
282 region_sync_changed ();
283 region_resized (ARDOUR::bounds_change);
288 _enable_display = true;
291 display_model (_model);
295 reset_width_dependent_items (_pixel_width);
297 group->raise_to_top();
299 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
300 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
303 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
304 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
306 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
307 boost::bind (&MidiRegionView::snap_changed, this),
310 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
311 connect_to_diskstream ();
313 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
317 MidiRegionView::instrument_info () const
319 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
320 return route_ui->route()->instrument_info();
323 const boost::shared_ptr<ARDOUR::MidiRegion>
324 MidiRegionView::midi_region() const
326 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
330 MidiRegionView::connect_to_diskstream ()
332 midi_view()->midi_track()->DataRecorded.connect(
333 *this, invalidator(*this),
334 boost::bind (&MidiRegionView::data_recorded, this, _1),
339 MidiRegionView::canvas_group_event(GdkEvent* ev)
345 if (!trackview.editor().internal_editing()) {
346 // not in internal edit mode, so just act like a normal region
347 return RegionView::canvas_group_event (ev);
350 const MouseMode m = trackview.editor().current_mouse_mode();
354 case GDK_ENTER_NOTIFY:
355 _last_event_x = ev->crossing.x;
356 _last_event_y = ev->crossing.y;
357 enter_notify(&ev->crossing);
358 // set entered_regionview (among other things)
359 return RegionView::canvas_group_event (ev);
361 case GDK_LEAVE_NOTIFY:
362 _last_event_x = ev->crossing.x;
363 _last_event_y = ev->crossing.y;
364 leave_notify(&ev->crossing);
365 // reset entered_regionview (among other things)
366 return RegionView::canvas_group_event (ev);
368 case GDK_2BUTTON_PRESS:
369 // cannot use double-click to exit internal mode if single-click is being used
370 if ((m != MouseDraw) &&
372 !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier()))) {
373 return trackview.editor().toggle_internal_editing_from_double_click (ev);
378 if (scroll (&ev->scroll)) {
384 return key_press (&ev->key);
386 case GDK_KEY_RELEASE:
387 return key_release (&ev->key);
389 case GDK_BUTTON_PRESS:
390 return button_press (&ev->button);
392 case GDK_BUTTON_RELEASE:
393 r = button_release (&ev->button);
398 case GDK_MOTION_NOTIFY:
399 _last_event_x = ev->motion.x;
400 _last_event_y = ev->motion.y;
401 return motion (&ev->motion);
407 return RegionView::canvas_group_event (ev);
411 MidiRegionView::enter_notify (GdkEventCrossing* ev)
413 trackview.editor().MouseModeChanged.connect (
414 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
424 MidiRegionView::leave_notify (GdkEventCrossing*)
426 _mouse_mode_connection.disconnect ();
435 MidiRegionView::mouse_mode_changed ()
437 if (trackview.editor().internal_editing()) {
438 // Switched in to internal editing mode while entered
441 // Switched out of internal editing mode while entered
447 MidiRegionView::enter_internal()
449 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
450 // Show ghost note under pencil
451 create_ghost_note(_last_event_x, _last_event_y);
454 if (!_selection.empty()) {
455 // Grab keyboard for moving selected notes with arrow keys
456 Keyboard::magic_widget_grab_focus();
457 _grabbed_keyboard = true;
462 MidiRegionView::leave_internal()
464 trackview.editor().verbose_cursor()->hide ();
465 remove_ghost_note ();
467 if (_grabbed_keyboard) {
468 Keyboard::magic_widget_drop_focus();
469 _grabbed_keyboard = false;
474 MidiRegionView::button_press (GdkEventButton* ev)
476 if (ev->button != 1) {
480 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
481 MouseMode m = editor->current_mouse_mode();
483 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
484 pre_press_cursor = editor->get_canvas_cursor ();
485 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
488 if (_mouse_state != SelectTouchDragging) {
490 _pressed_button = ev->button;
491 _mouse_state = Pressed;
496 _pressed_button = ev->button;
502 MidiRegionView::button_release (GdkEventButton* ev)
504 double event_x, event_y;
506 if (ev->button != 1) {
513 group->canvas_to_item (event_x, event_y);
516 PublicEditor& editor = trackview.editor ();
518 if (pre_press_cursor) {
519 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
520 pre_press_cursor = 0;
523 switch (_mouse_state) {
524 case Pressed: // Clicked
526 switch (editor.current_mouse_mode()) {
528 /* no motion occured - simple click */
537 if (Keyboard::is_insert_note_event(ev)) {
539 double event_x, event_y;
543 group->canvas_to_item (event_x, event_y);
545 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
547 /* Shorten the length by 1 tick so that we can add a new note at the next
548 grid snap without it overlapping this one.
550 beats -= Evoral::MusicalTime::tick();
552 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
559 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
561 /* Shorten the length by 1 tick so that we can add a new note at the next
562 grid snap without it overlapping this one.
564 beats -= Evoral::MusicalTime::tick();
566 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
577 case SelectRectDragging:
579 editor.drags()->end_grab ((GdkEvent *) ev);
581 create_ghost_note (ev->x, ev->y);
593 MidiRegionView::motion (GdkEventMotion* ev)
595 PublicEditor& editor = trackview.editor ();
597 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
598 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
599 _mouse_state != AddDragging) {
601 create_ghost_note (ev->x, ev->y);
603 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
604 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
606 update_ghost_note (ev->x, ev->y);
608 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
610 remove_ghost_note ();
611 editor.verbose_cursor()->hide ();
613 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
615 update_ghost_note (ev->x, ev->y);
618 /* any motion immediately hides velocity text that may have been visible */
620 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
621 (*i)->hide_velocity ();
624 switch (_mouse_state) {
627 if (_pressed_button == 1) {
629 MouseMode m = editor.current_mouse_mode();
631 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
632 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
633 _mouse_state = AddDragging;
634 remove_ghost_note ();
635 editor.verbose_cursor()->hide ();
637 } else if (m == MouseObject) {
638 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
640 _mouse_state = SelectRectDragging;
642 } else if (m == MouseRange) {
643 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
644 _mouse_state = SelectVerticalDragging;
651 case SelectRectDragging:
652 case SelectVerticalDragging:
654 editor.drags()->motion_handler ((GdkEvent *) ev, false);
657 case SelectTouchDragging:
665 /* we may be dragging some non-note object (eg. patch-change, sysex)
668 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
673 MidiRegionView::scroll (GdkEventScroll* ev)
675 if (_selection.empty()) {
679 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
680 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
681 it still works for zoom.
686 trackview.editor().verbose_cursor()->hide ();
688 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
689 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
691 if (ev->direction == GDK_SCROLL_UP) {
692 change_velocities (true, fine, false, together);
693 } else if (ev->direction == GDK_SCROLL_DOWN) {
694 change_velocities (false, fine, false, together);
696 /* left, right: we don't use them */
704 MidiRegionView::key_press (GdkEventKey* ev)
706 /* since GTK bindings are generally activated on press, and since
707 detectable auto-repeat is the name of the game and only sends
708 repeated presses, carry out key actions at key press, not release.
711 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
713 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
714 _mouse_state = SelectTouchDragging;
717 } else if (ev->keyval == GDK_Escape && unmodified) {
721 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
723 bool start = (ev->keyval == GDK_comma);
724 bool end = (ev->keyval == GDK_period);
725 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
726 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
728 change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
732 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
734 if (_selection.empty()) {
741 } else if (ev->keyval == GDK_Tab) {
743 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
744 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
746 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
750 } else if (ev->keyval == GDK_ISO_Left_Tab) {
752 /* Shift-TAB generates ISO Left Tab, for some reason */
754 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
755 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
757 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
763 } else if (ev->keyval == GDK_Up) {
765 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
766 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
767 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
769 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
770 change_velocities (true, fine, allow_smush, together);
772 transpose (true, fine, allow_smush);
776 } else if (ev->keyval == GDK_Down) {
778 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
779 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
780 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
782 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
783 change_velocities (false, fine, allow_smush, together);
785 transpose (false, fine, allow_smush);
789 } else if (ev->keyval == GDK_Left) {
791 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
792 nudge_notes (false, fine);
795 } else if (ev->keyval == GDK_Right) {
797 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
798 nudge_notes (true, fine);
801 } else if (ev->keyval == GDK_c && unmodified) {
805 } else if (ev->keyval == GDK_v && unmodified) {
814 MidiRegionView::key_release (GdkEventKey* ev)
816 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
824 MidiRegionView::channel_edit ()
826 if (_selection.empty()) {
830 /* pick a note somewhat at random (since Selection is a set<>) to
831 * provide the "current" channel for the dialog.
834 uint8_t current_channel = (*_selection.begin())->note()->channel ();
835 MidiChannelDialog channel_dialog (current_channel);
836 int ret = channel_dialog.run ();
839 case Gtk::RESPONSE_OK:
845 uint8_t new_channel = channel_dialog.active_channel ();
847 start_note_diff_command (_("channel edit"));
849 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
850 Selection::iterator next = i;
852 change_note_channel (*i, new_channel);
860 MidiRegionView::velocity_edit ()
862 if (_selection.empty()) {
866 /* pick a note somewhat at random (since Selection is a set<>) to
867 * provide the "current" velocity for the dialog.
870 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
871 MidiVelocityDialog velocity_dialog (current_velocity);
872 int ret = velocity_dialog.run ();
875 case Gtk::RESPONSE_OK:
881 uint8_t new_velocity = velocity_dialog.velocity ();
883 start_note_diff_command (_("velocity edit"));
885 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
886 Selection::iterator next = i;
888 change_note_velocity (*i, new_velocity, false);
896 MidiRegionView::show_list_editor ()
899 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
901 _list_editor->present ();
904 /** Add a note to the model, and the view, at a canvas (click) coordinate.
905 * \param t time in frames relative to the position of the region
906 * \param y vertical position in pixels
907 * \param length duration of the note in beats
908 * \param snap_t true to snap t to the grid, otherwise false.
911 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
913 if (length < 2 * DBL_EPSILON) {
917 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
918 MidiStreamView* const view = mtv->midi_view();
920 const double note = view->y_to_note(y);
922 // Start of note in frames relative to region start
924 framecnt_t grid_frames;
925 t = snap_frame_to_grid_underneath (t, grid_frames);
928 const boost::shared_ptr<NoteType> new_note (
929 new NoteType (mtv->get_channel_for_add (),
930 region_frames_to_region_beats(t + _region->start()),
932 (uint8_t)note, 0x40));
934 if (_model->contains (new_note)) {
938 view->update_note_range(new_note->note());
940 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
942 _model->apply_command(*trackview.session(), cmd);
944 play_midi_note (new_note);
948 MidiRegionView::clear_events (bool with_selection_signal)
950 clear_selection (with_selection_signal);
953 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
954 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
959 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
964 _patch_changes.clear();
966 _optimization_iterator = _events.end();
970 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
974 content_connection.disconnect ();
975 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
979 if (_enable_display) {
985 MidiRegionView::start_note_diff_command (string name)
987 if (!_note_diff_command) {
988 _note_diff_command = _model->new_note_diff_command (name);
993 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
995 if (_note_diff_command) {
996 _note_diff_command->add (note);
999 _marked_for_selection.insert(note);
1001 if (show_velocity) {
1002 _marked_for_velocity.insert(note);
1007 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1009 if (_note_diff_command && ev->note()) {
1010 _note_diff_command->remove(ev->note());
1015 MidiRegionView::note_diff_add_change (NoteBase* ev,
1016 MidiModel::NoteDiffCommand::Property property,
1019 if (_note_diff_command) {
1020 _note_diff_command->change (ev->note(), property, val);
1025 MidiRegionView::note_diff_add_change (NoteBase* ev,
1026 MidiModel::NoteDiffCommand::Property property,
1027 Evoral::MusicalTime val)
1029 if (_note_diff_command) {
1030 _note_diff_command->change (ev->note(), property, val);
1035 MidiRegionView::apply_diff (bool as_subcommand)
1039 if (!_note_diff_command) {
1043 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1044 // Mark all selected notes for selection when model reloads
1045 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1046 _marked_for_selection.insert((*i)->note());
1050 if (as_subcommand) {
1051 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1053 _model->apply_command (*trackview.session(), _note_diff_command);
1056 _note_diff_command = 0;
1057 midi_view()->midi_track()->playlist_modified();
1059 if (add_or_remove) {
1060 _marked_for_selection.clear();
1063 _marked_for_velocity.clear();
1067 MidiRegionView::abort_command()
1069 delete _note_diff_command;
1070 _note_diff_command = 0;
1075 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1077 if (_optimization_iterator != _events.end()) {
1078 ++_optimization_iterator;
1081 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1082 return *_optimization_iterator;
1085 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1086 if ((*_optimization_iterator)->note() == note) {
1087 return *_optimization_iterator;
1095 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1097 MidiModel::Notes notes;
1098 _model->get_notes (notes, op, val, chan_mask);
1100 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1101 NoteBase* cne = find_canvas_note (*n);
1109 MidiRegionView::redisplay_model()
1111 // Don't redisplay the model if we're currently recording and displaying that
1112 if (_active_notes) {
1120 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1121 (*i)->invalidate ();
1124 MidiModel::ReadLock lock(_model->read_lock());
1126 MidiModel::Notes& notes (_model->notes());
1127 _optimization_iterator = _events.begin();
1129 bool empty_when_starting = _events.empty();
1131 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1133 boost::shared_ptr<NoteType> note (*n);
1137 if (note_in_region_range (note, visible)) {
1139 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1146 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1148 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1160 add_note (note, visible);
1165 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1173 /* remove note items that are no longer valid */
1175 if (!empty_when_starting) {
1176 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1177 if (!(*i)->valid ()) {
1179 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1180 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1182 gr->remove_note (*i);
1187 i = _events.erase (i);
1195 _patch_changes.clear();
1199 display_patch_changes ();
1201 _marked_for_selection.clear ();
1202 _marked_for_velocity.clear ();
1204 /* we may have caused _events to contain things out of order (e.g. if a note
1205 moved earlier or later). we don't generally need them in time order, but
1206 make a note that a sort is required for those cases that require it.
1209 _sort_needed = true;
1213 MidiRegionView::display_patch_changes ()
1215 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1216 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1218 for (uint8_t i = 0; i < 16; ++i) {
1219 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1223 /** @param active_channel true to display patch changes fully, false to display
1224 * them `greyed-out' (as on an inactive channel)
1227 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1229 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1231 if ((*i)->channel() != channel) {
1235 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1236 add_canvas_patch_change (*i, patch_name, active_channel);
1241 MidiRegionView::display_sysexes()
1243 bool have_periodic_system_messages = false;
1244 bool display_periodic_messages = true;
1246 if (!Config->get_never_display_periodic_midi()) {
1248 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1249 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1250 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1253 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1254 have_periodic_system_messages = true;
1260 if (have_periodic_system_messages) {
1261 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1263 /* get an approximate value for the number of samples per video frame */
1265 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1267 /* if we are zoomed out beyond than the cutoff (i.e. more
1268 * frames per pixel than frames per 4 video frames), don't
1269 * show periodic sysex messages.
1272 if (zoom > (video_frame*4)) {
1273 display_periodic_messages = false;
1277 display_periodic_messages = false;
1280 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1282 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1283 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1285 Evoral::MusicalTime time = (*i)->time();
1288 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1289 if (!display_periodic_messages) {
1297 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1298 str << int((*i)->buffer()[b]);
1299 if (b != (*i)->size() -1) {
1303 string text = str.str();
1305 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1307 double height = midi_stream_view()->contents_height();
1309 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1310 // SysEx canvas object!!!
1312 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1313 new SysEx (*this, _note_group, text, height, x, 1.0));
1315 // Show unless message is beyond the region bounds
1316 if (time - _region->start() >= _region->length() || time < _region->start()) {
1322 _sys_exes.push_back(sysex);
1326 MidiRegionView::~MidiRegionView ()
1328 in_destructor = true;
1330 trackview.editor().verbose_cursor()->hide ();
1332 note_delete_connection.disconnect ();
1334 delete _list_editor;
1336 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1338 if (_active_notes) {
1342 _selection_cleared_connection.disconnect ();
1345 clear_events (false);
1348 delete _note_diff_command;
1349 delete _step_edit_cursor;
1350 delete _temporary_note_group;
1354 MidiRegionView::region_resized (const PropertyChange& what_changed)
1356 RegionView::region_resized(what_changed);
1358 if (what_changed.contains (ARDOUR::Properties::position)) {
1359 _region_relative_time_converter.set_origin_b(_region->position());
1360 set_duration(_region->length(), 0);
1361 if (_enable_display) {
1366 if (what_changed.contains (ARDOUR::Properties::start) ||
1367 what_changed.contains (ARDOUR::Properties::position)) {
1368 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1373 MidiRegionView::reset_width_dependent_items (double pixel_width)
1375 RegionView::reset_width_dependent_items(pixel_width);
1377 if (_enable_display) {
1381 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1382 if ((*x)->canvas_item()->width() >= _pixel_width) {
1389 move_step_edit_cursor (_step_edit_cursor_position);
1390 set_step_edit_cursor_width (_step_edit_cursor_width);
1394 MidiRegionView::set_height (double height)
1396 double old_height = _height;
1397 RegionView::set_height(height);
1399 apply_note_range (midi_stream_view()->lowest_note(),
1400 midi_stream_view()->highest_note(),
1401 height != old_height);
1404 name_text->raise_to_top();
1407 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1408 (*x)->set_height (midi_stream_view()->contents_height());
1411 if (_step_edit_cursor) {
1412 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1417 /** Apply the current note range from the stream view
1418 * by repositioning/hiding notes as necessary
1421 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1423 if (!_enable_display) {
1427 if (!force && _current_range_min == min && _current_range_max == max) {
1431 _current_range_min = min;
1432 _current_range_max = max;
1434 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1435 NoteBase* event = *i;
1436 boost::shared_ptr<NoteType> note (event->note());
1438 if (note->note() < _current_range_min ||
1439 note->note() > _current_range_max) {
1445 if (Note* cnote = dynamic_cast<Note*>(event)) {
1447 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1448 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1453 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1460 MidiRegionView::add_ghost (TimeAxisView& tv)
1464 double unit_position = _region->position () / samples_per_pixel;
1465 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1466 MidiGhostRegion* ghost;
1468 if (mtv && mtv->midi_view()) {
1469 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1470 to allow having midi notes on top of note lines and waveforms.
1472 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1474 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1477 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1478 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1479 ghost->add_note(note);
1483 ghost->set_height ();
1484 ghost->set_duration (_region->length() / samples_per_pixel);
1485 ghosts.push_back (ghost);
1487 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1493 /** Begin tracking note state for successive calls to add_event
1496 MidiRegionView::begin_write()
1498 if (_active_notes) {
1499 delete[] _active_notes;
1501 _active_notes = new Note*[128];
1502 for (unsigned i = 0; i < 128; ++i) {
1503 _active_notes[i] = 0;
1508 /** Destroy note state for add_event
1511 MidiRegionView::end_write()
1513 delete[] _active_notes;
1515 _marked_for_selection.clear();
1516 _marked_for_velocity.clear();
1520 /** Resolve an active MIDI note (while recording).
1523 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1525 if (midi_view()->note_mode() != Sustained) {
1529 if (_active_notes && _active_notes[note]) {
1531 /* XXX is end_time really region-centric? I think so, because
1532 this is a new region that we're recording, so source zero is
1533 the same as region zero
1535 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1537 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1538 _active_notes[note]->set_outline_all ();
1539 _active_notes[note] = 0;
1545 /** Extend active notes to rightmost edge of region (if length is changed)
1548 MidiRegionView::extend_active_notes()
1550 if (!_active_notes) {
1554 for (unsigned i=0; i < 128; ++i) {
1555 if (_active_notes[i]) {
1556 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1563 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1565 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1569 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1571 if (!route_ui || !route_ui->midi_track()) {
1575 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1579 /* NotePlayer deletes itself */
1583 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1585 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1589 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1591 if (!route_ui || !route_ui->midi_track()) {
1595 delete _note_player;
1596 _note_player = new NotePlayer (route_ui->midi_track ());
1597 _note_player->add (note);
1598 _note_player->on ();
1602 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1604 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1608 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1610 if (!route_ui || !route_ui->midi_track()) {
1614 delete _note_player;
1615 _note_player = new NotePlayer (route_ui->midi_track());
1617 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1618 _note_player->add (*n);
1621 _note_player->on ();
1626 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1628 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1629 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1631 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1632 (note->note() <= midi_stream_view()->highest_note());
1637 /** Update a canvas note's size from its model note.
1638 * @param ev Canvas note to update.
1639 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1642 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1644 boost::shared_ptr<NoteType> note = ev->note();
1645 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1646 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1651 /* trim note display to not overlap the end of its region */
1653 if (note->length() > 0) {
1654 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1655 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1657 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1660 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1662 if (!note->length()) {
1663 if (_active_notes && note->note() < 128) {
1664 // If this note is already active there's a stuck note,
1665 // finish the old note rectangle
1666 if (_active_notes[note->note()]) {
1667 Note* const old_rect = _active_notes[note->note()];
1668 boost::shared_ptr<NoteType> old_note = old_rect->note();
1669 old_rect->set_x1 (x);
1670 old_rect->set_outline_all ();
1672 _active_notes[note->note()] = ev;
1674 /* outline all but right edge */
1675 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1676 ArdourCanvas::Rectangle::TOP|
1677 ArdourCanvas::Rectangle::LEFT|
1678 ArdourCanvas::Rectangle::BOTTOM));
1680 /* outline all edges */
1681 ev->set_outline_all ();
1684 if (update_ghost_regions) {
1685 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1686 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1688 gr->update_note (ev);
1695 MidiRegionView::update_hit (Hit* ev)
1697 boost::shared_ptr<NoteType> note = ev->note();
1699 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1700 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1701 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1702 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1704 ev->set_position (ArdourCanvas::Duple (x, y));
1705 ev->set_height (diamond_size);
1708 /** Add a MIDI note to the view (with length).
1710 * If in sustained mode, notes with length 0 will be considered active
1711 * notes, and resolve_note should be called when the corresponding note off
1712 * event arrives, to properly display the note.
1715 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1717 NoteBase* event = 0;
1719 if (midi_view()->note_mode() == Sustained) {
1721 Note* ev_rect = new Note (*this, _note_group, note);
1723 update_note (ev_rect);
1727 MidiGhostRegion* gr;
1729 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1730 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1731 gr->add_note(ev_rect);
1735 } else if (midi_view()->note_mode() == Percussive) {
1737 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1739 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1741 update_hit (ev_diamond);
1750 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1751 note_selected(event, true);
1754 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1755 event->show_velocity();
1758 event->on_channel_selection_change (get_selected_channels());
1759 _events.push_back(event);
1768 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1769 MidiStreamView* const view = mtv->midi_view();
1771 view->update_note_range (note->note());
1775 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1776 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1778 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1780 /* potentially extend region to hold new note */
1782 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1783 framepos_t region_end = _region->last_frame();
1785 if (end_frame > region_end) {
1786 _region->set_length (end_frame - _region->position());
1789 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1790 MidiStreamView* const view = mtv->midi_view();
1792 view->update_note_range(new_note->note());
1794 _marked_for_selection.clear ();
1797 start_note_diff_command (_("step add"));
1798 note_diff_add_note (new_note, true, false);
1801 // last_step_edit_note = new_note;
1805 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1807 change_note_lengths (false, false, beats, false, true);
1810 /** Add a new patch change flag to the canvas.
1811 * @param patch the patch change to add
1812 * @param the text to display in the flag
1813 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1816 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1818 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1819 const double x = trackview.editor().sample_to_pixel (region_frames);
1821 double const height = midi_stream_view()->contents_height();
1823 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1824 // so we need to do something more sophisticated to keep its color
1825 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1828 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1829 new PatchChange(*this, group,
1836 if (patch_change->item().width() < _pixel_width) {
1837 // Show unless patch change is beyond the region bounds
1838 if (region_frames < 0 || region_frames >= _region->length()) {
1839 patch_change->hide();
1841 patch_change->show();
1844 patch_change->hide ();
1847 _patch_changes.push_back (patch_change);
1850 MIDI::Name::PatchPrimaryKey
1851 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1853 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1856 /// Return true iff @p pc applies to the given time on the given channel.
1858 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1860 return pc->time() <= time && pc->channel() == channel;
1864 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1866 // The earliest event not before time
1867 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1869 // Go backwards until we find the latest PC for this channel, or the start
1870 while (i != _model->patch_changes().begin() &&
1871 (i == _model->patch_changes().end() ||
1872 !patch_applies(*i, time, channel))) {
1876 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1877 key.bank_number = (*i)->bank();
1878 key.program_number = (*i)->program ();
1880 key.bank_number = key.program_number = 0;
1883 if (!key.is_sane()) {
1884 error << string_compose(_("insane MIDI patch key %1:%2"),
1885 key.bank_number, key.program_number) << endmsg;
1890 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1892 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1894 if (pc.patch()->program() != new_patch.program_number) {
1895 c->change_program (pc.patch (), new_patch.program_number);
1898 int const new_bank = new_patch.bank_number;
1899 if (pc.patch()->bank() != new_bank) {
1900 c->change_bank (pc.patch (), new_bank);
1903 _model->apply_command (*trackview.session(), c);
1905 _patch_changes.clear ();
1906 display_patch_changes ();
1910 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1912 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1914 if (old_change->time() != new_change.time()) {
1915 c->change_time (old_change, new_change.time());
1918 if (old_change->channel() != new_change.channel()) {
1919 c->change_channel (old_change, new_change.channel());
1922 if (old_change->program() != new_change.program()) {
1923 c->change_program (old_change, new_change.program());
1926 if (old_change->bank() != new_change.bank()) {
1927 c->change_bank (old_change, new_change.bank());
1930 _model->apply_command (*trackview.session(), c);
1932 _patch_changes.clear ();
1933 display_patch_changes ();
1936 /** Add a patch change to the region.
1937 * @param t Time in frames relative to region position
1938 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1939 * MidiTimeAxisView::get_channel_for_add())
1942 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1944 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1946 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1947 c->add (MidiModel::PatchChangePtr (
1948 new Evoral::PatchChange<Evoral::MusicalTime> (
1949 absolute_frames_to_source_beats (_region->position() + t),
1950 mtv->get_channel_for_add(), patch.program(), patch.bank()
1955 _model->apply_command (*trackview.session(), c);
1957 _patch_changes.clear ();
1958 display_patch_changes ();
1962 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1964 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1965 c->change_time (pc.patch (), t);
1966 _model->apply_command (*trackview.session(), c);
1968 _patch_changes.clear ();
1969 display_patch_changes ();
1973 MidiRegionView::delete_patch_change (PatchChange* pc)
1975 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1976 c->remove (pc->patch ());
1977 _model->apply_command (*trackview.session(), c);
1979 _patch_changes.clear ();
1980 display_patch_changes ();
1984 MidiRegionView::previous_patch (PatchChange& patch)
1986 if (patch.patch()->program() < 127) {
1987 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1988 key.program_number++;
1989 change_patch_change (patch, key);
1994 MidiRegionView::next_patch (PatchChange& patch)
1996 if (patch.patch()->program() > 0) {
1997 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1998 key.program_number--;
1999 change_patch_change (patch, key);
2004 MidiRegionView::next_bank (PatchChange& patch)
2006 if (patch.patch()->program() < 127) {
2007 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2008 if (key.bank_number > 0) {
2010 change_patch_change (patch, key);
2016 MidiRegionView::previous_bank (PatchChange& patch)
2018 if (patch.patch()->program() > 0) {
2019 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2020 if (key.bank_number < 127) {
2022 change_patch_change (patch, key);
2028 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2030 if (_selection.empty()) {
2034 _selection.erase (cne);
2038 MidiRegionView::delete_selection()
2040 if (_selection.empty()) {
2044 start_note_diff_command (_("delete selection"));
2046 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2047 if ((*i)->selected()) {
2048 _note_diff_command->remove((*i)->note());
2058 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2060 start_note_diff_command (_("delete note"));
2061 _note_diff_command->remove (n);
2064 trackview.editor().verbose_cursor()->hide ();
2068 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2070 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2072 Selection::iterator tmp = i;
2075 (*i)->set_selected (false);
2076 (*i)->hide_velocity ();
2077 _selection.erase (i);
2085 if (!ev && _entered) {
2086 // Clearing selection entirely, ungrab keyboard
2087 Keyboard::magic_widget_drop_focus();
2088 _grabbed_keyboard = false;
2091 /* this does not change the status of this regionview w.r.t the editor
2096 SelectionCleared (this); /* EMIT SIGNAL */
2101 MidiRegionView::unique_select(NoteBase* ev)
2103 const bool selection_was_empty = _selection.empty();
2105 clear_selection_except (ev);
2107 /* don't bother with checking to see if we should remove this
2108 regionview from the editor selection, since we're about to add
2109 another note, and thus put/keep this regionview in the editor
2113 if (!ev->selected()) {
2114 add_to_selection (ev);
2115 if (selection_was_empty && _entered) {
2116 // Grab keyboard for moving notes with arrow keys
2117 Keyboard::magic_widget_grab_focus();
2118 _grabbed_keyboard = true;
2124 MidiRegionView::select_all_notes ()
2128 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2129 add_to_selection (*i);
2134 MidiRegionView::select_range (framepos_t start, framepos_t end)
2138 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2139 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2140 if (t >= start && t <= end) {
2141 add_to_selection (*i);
2147 MidiRegionView::invert_selection ()
2149 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2150 if ((*i)->selected()) {
2151 remove_from_selection(*i);
2153 add_to_selection (*i);
2159 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2161 bool have_selection = !_selection.empty();
2162 uint8_t low_note = 127;
2163 uint8_t high_note = 0;
2164 MidiModel::Notes& notes (_model->notes());
2165 _optimization_iterator = _events.begin();
2167 if (extend && !have_selection) {
2171 /* scan existing selection to get note range */
2173 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2174 if ((*i)->note()->note() < low_note) {
2175 low_note = (*i)->note()->note();
2177 if ((*i)->note()->note() > high_note) {
2178 high_note = (*i)->note()->note();
2185 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2186 /* only note previously selected is the one we are
2187 * reselecting. treat this as cancelling the selection.
2194 low_note = min (low_note, notenum);
2195 high_note = max (high_note, notenum);
2198 _no_sound_notes = true;
2200 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2202 boost::shared_ptr<NoteType> note (*n);
2204 bool select = false;
2206 if (((1 << note->channel()) & channel_mask) != 0) {
2208 if ((note->note() >= low_note && note->note() <= high_note)) {
2211 } else if (note->note() == notenum) {
2217 if ((cne = find_canvas_note (note)) != 0) {
2218 // extend is false because we've taken care of it,
2219 // since it extends by time range, not pitch.
2220 note_selected (cne, add, false);
2224 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2228 _no_sound_notes = false;
2232 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2234 MidiModel::Notes& notes (_model->notes());
2235 _optimization_iterator = _events.begin();
2237 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2239 boost::shared_ptr<NoteType> note (*n);
2242 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2243 if ((cne = find_canvas_note (note)) != 0) {
2244 if (cne->selected()) {
2245 note_deselected (cne);
2247 note_selected (cne, true, false);
2255 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2258 clear_selection_except (ev);
2259 if (!_selection.empty()) {
2260 PublicEditor& editor (trackview.editor());
2261 editor.get_selection().add (this);
2267 if (!ev->selected()) {
2268 add_to_selection (ev);
2272 /* find end of latest note selected, select all between that and the start of "ev" */
2274 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2275 Evoral::MusicalTime latest = Evoral::MusicalTime();
2277 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2278 if ((*i)->note()->end_time() > latest) {
2279 latest = (*i)->note()->end_time();
2281 if ((*i)->note()->time() < earliest) {
2282 earliest = (*i)->note()->time();
2286 if (ev->note()->end_time() > latest) {
2287 latest = ev->note()->end_time();
2290 if (ev->note()->time() < earliest) {
2291 earliest = ev->note()->time();
2294 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2296 /* find notes entirely within OR spanning the earliest..latest range */
2298 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2299 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2300 add_to_selection (*i);
2308 MidiRegionView::note_deselected(NoteBase* ev)
2310 remove_from_selection (ev);
2314 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2316 PublicEditor& editor = trackview.editor();
2318 // Convert to local coordinates
2319 const framepos_t p = _region->position();
2320 const double y = midi_view()->y_position();
2321 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2322 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2323 const double y0 = max(0.0, gy0 - y);
2324 const double y1 = max(0.0, gy1 - y);
2326 // TODO: Make this faster by storing the last updated selection rect, and only
2327 // adjusting things that are in the area that appears/disappeared.
2328 // We probably need a tree to be able to find events in O(log(n)) time.
2330 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2331 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2332 // Rectangles intersect
2333 if (!(*i)->selected()) {
2334 add_to_selection (*i);
2336 } else if ((*i)->selected() && !extend) {
2337 // Rectangles do not intersect
2338 remove_from_selection (*i);
2342 typedef RouteTimeAxisView::AutomationTracks ATracks;
2343 typedef std::list<Selectable*> Selectables;
2345 /* Add control points to selection. */
2346 const ATracks& atracks = midi_view()->automation_tracks();
2347 Selectables selectables;
2348 editor.get_selection().clear_points();
2349 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2350 a->second->get_selectables(start, end, gy0, gy1, selectables);
2351 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2352 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2354 editor.get_selection().add(cp);
2357 a->second->set_selected_points(editor.get_selection().points);
2362 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2368 // TODO: Make this faster by storing the last updated selection rect, and only
2369 // adjusting things that are in the area that appears/disappeared.
2370 // We probably need a tree to be able to find events in O(log(n)) time.
2372 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2373 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2374 // within y- (note-) range
2375 if (!(*i)->selected()) {
2376 add_to_selection (*i);
2378 } else if ((*i)->selected() && !extend) {
2379 remove_from_selection (*i);
2385 MidiRegionView::remove_from_selection (NoteBase* ev)
2387 Selection::iterator i = _selection.find (ev);
2389 if (i != _selection.end()) {
2390 _selection.erase (i);
2391 if (_selection.empty() && _grabbed_keyboard) {
2393 Keyboard::magic_widget_drop_focus();
2394 _grabbed_keyboard = false;
2398 ev->set_selected (false);
2399 ev->hide_velocity ();
2401 if (_selection.empty()) {
2402 PublicEditor& editor (trackview.editor());
2403 editor.get_selection().remove (this);
2408 MidiRegionView::add_to_selection (NoteBase* ev)
2410 const bool selection_was_empty = _selection.empty();
2412 if (_selection.insert (ev).second) {
2413 ev->set_selected (true);
2414 start_playing_midi_note ((ev)->note());
2415 if (selection_was_empty && _entered) {
2416 // Grab keyboard for moving notes with arrow keys
2417 Keyboard::magic_widget_grab_focus();
2418 _grabbed_keyboard = true;
2422 if (selection_was_empty) {
2423 PublicEditor& editor (trackview.editor());
2424 editor.get_selection().add (this);
2429 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2431 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2432 PossibleChord to_play;
2433 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2435 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2436 if ((*i)->note()->time() < earliest) {
2437 earliest = (*i)->note()->time();
2441 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2442 if ((*i)->note()->time() == earliest) {
2443 to_play.push_back ((*i)->note());
2445 (*i)->move_event(dx, dy);
2448 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2450 if (to_play.size() > 1) {
2452 PossibleChord shifted;
2454 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2455 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2456 moved_note->set_note (moved_note->note() + cumulative_dy);
2457 shifted.push_back (moved_note);
2460 start_playing_midi_chord (shifted);
2462 } else if (!to_play.empty()) {
2464 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2465 moved_note->set_note (moved_note->note() + cumulative_dy);
2466 start_playing_midi_note (moved_note);
2472 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2474 uint8_t lowest_note_in_selection = 127;
2475 uint8_t highest_note_in_selection = 0;
2476 uint8_t highest_note_difference = 0;
2478 // find highest and lowest notes first
2480 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2481 uint8_t pitch = (*i)->note()->note();
2482 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2483 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2487 cerr << "dnote: " << (int) dnote << endl;
2488 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2489 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2490 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2491 << int(highest_note_in_selection) << endl;
2492 cerr << "selection size: " << _selection.size() << endl;
2493 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2496 // Make sure the note pitch does not exceed the MIDI standard range
2497 if (highest_note_in_selection + dnote > 127) {
2498 highest_note_difference = highest_note_in_selection - 127;
2501 start_note_diff_command (_("move notes"));
2503 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2505 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2506 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2512 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2514 uint8_t original_pitch = (*i)->note()->note();
2515 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2517 // keep notes in standard midi range
2518 clamp_to_0_127(new_pitch);
2520 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2521 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2523 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2528 // care about notes being moved beyond the upper/lower bounds on the canvas
2529 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2530 highest_note_in_selection > midi_stream_view()->highest_note()) {
2531 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2535 /** @param x Pixel relative to the region position.
2536 * @return Snapped frame relative to the region position.
2539 MidiRegionView::snap_pixel_to_sample(double x)
2541 PublicEditor& editor (trackview.editor());
2542 return snap_frame_to_frame (editor.pixel_to_sample (x));
2545 /** @param x Pixel relative to the region position.
2546 * @return Snapped pixel relative to the region position.
2549 MidiRegionView::snap_to_pixel(double x)
2551 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2555 MidiRegionView::get_position_pixels()
2557 framepos_t region_frame = get_position();
2558 return trackview.editor().sample_to_pixel(region_frame);
2562 MidiRegionView::get_end_position_pixels()
2564 framepos_t frame = get_position() + get_duration ();
2565 return trackview.editor().sample_to_pixel(frame);
2569 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2571 /* the time converter will return the frame corresponding to `beats'
2572 relative to the start of the source. The start of the source
2573 is an implied position given by region->position - region->start
2575 const framepos_t source_start = _region->position() - _region->start();
2576 return source_start + _source_relative_time_converter.to (beats);
2580 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2582 /* the `frames' argument needs to be converted into a frame count
2583 relative to the start of the source before being passed in to the
2586 const framepos_t source_start = _region->position() - _region->start();
2587 return _source_relative_time_converter.from (frames - source_start);
2591 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2593 return _region_relative_time_converter.to(beats);
2597 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2599 return _region_relative_time_converter.from(frames);
2603 MidiRegionView::begin_resizing (bool /*at_front*/)
2605 _resize_data.clear();
2607 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2608 Note *note = dynamic_cast<Note*> (*i);
2610 // only insert CanvasNotes into the map
2612 NoteResizeData *resize_data = new NoteResizeData();
2613 resize_data->note = note;
2615 // create a new SimpleRect from the note which will be the resize preview
2616 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2617 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2619 // calculate the colors: get the color settings
2620 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2621 ARDOUR_UI::config()->get_MidiNoteSelected(),
2624 // make the resize preview notes more transparent and bright
2625 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2627 // calculate color based on note velocity
2628 resize_rect->set_fill_color (UINT_INTERPOLATE(
2629 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2633 resize_rect->set_outline_color (NoteBase::calculate_outline (
2634 ARDOUR_UI::config()->get_MidiNoteSelected()));
2636 resize_data->resize_rect = resize_rect;
2637 _resize_data.push_back(resize_data);
2642 /** Update resizing notes while user drags.
2643 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2644 * @param at_front which end of the note (true == note on, false == note off)
2645 * @param delta_x change in mouse position since the start of the drag
2646 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2647 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2648 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2649 * as the \a primary note.
2652 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2654 bool cursor_set = false;
2656 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2657 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2658 Note* canvas_note = (*i)->note;
2663 current_x = canvas_note->x0() + delta_x;
2665 current_x = primary->x0() + delta_x;
2669 current_x = canvas_note->x1() + delta_x;
2671 current_x = primary->x1() + delta_x;
2675 if (current_x < 0) {
2676 // This works even with snapping because RegionView::snap_frame_to_frame()
2677 // snaps forward if the snapped sample is before the beginning of the region
2680 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2681 current_x = trackview.editor().sample_to_pixel(_region->length());
2685 resize_rect->set_x0 (snap_to_pixel(current_x));
2686 resize_rect->set_x1 (canvas_note->x1());
2688 resize_rect->set_x1 (snap_to_pixel(current_x));
2689 resize_rect->set_x0 (canvas_note->x0());
2693 const double snapped_x = snap_pixel_to_sample (current_x);
2694 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2695 Evoral::MusicalTime len = Evoral::MusicalTime();
2698 if (beats < canvas_note->note()->end_time()) {
2699 len = canvas_note->note()->time() - beats;
2700 len += canvas_note->note()->length();
2703 if (beats >= canvas_note->note()->time()) {
2704 len = beats - canvas_note->note()->time();
2709 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2710 show_verbose_cursor (buf, 0, 0);
2719 /** Finish resizing notes when the user releases the mouse button.
2720 * Parameters the same as for \a update_resizing().
2723 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2725 start_note_diff_command (_("resize notes"));
2727 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2728 Note* canvas_note = (*i)->note;
2729 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2731 /* Get the new x position for this resize, which is in pixels relative
2732 * to the region position.
2739 current_x = canvas_note->x0() + delta_x;
2741 current_x = primary->x0() + delta_x;
2745 current_x = canvas_note->x1() + delta_x;
2747 current_x = primary->x1() + delta_x;
2751 if (current_x < 0) {
2754 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2755 current_x = trackview.editor().sample_to_pixel(_region->length());
2758 /* Convert that to a frame within the source */
2759 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2761 /* and then to beats */
2762 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2764 if (at_front && x_beats < canvas_note->note()->end_time()) {
2765 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2767 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2768 len += canvas_note->note()->length();
2771 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2776 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2779 /* XXX convert to beats */
2780 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2788 _resize_data.clear();
2793 MidiRegionView::abort_resizing ()
2795 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2796 delete (*i)->resize_rect;
2800 _resize_data.clear ();
2804 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2806 uint8_t new_velocity;
2809 new_velocity = event->note()->velocity() + velocity;
2810 clamp_to_0_127(new_velocity);
2812 new_velocity = velocity;
2815 event->set_selected (event->selected()); // change color
2817 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2821 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2826 new_note = event->note()->note() + note;
2831 clamp_to_0_127 (new_note);
2832 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2836 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2838 bool change_start = false;
2839 bool change_length = false;
2840 Evoral::MusicalTime new_start;
2841 Evoral::MusicalTime new_length;
2843 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2845 front_delta: if positive - move the start of the note later in time (shortening it)
2846 if negative - move the start of the note earlier in time (lengthening it)
2848 end_delta: if positive - move the end of the note later in time (lengthening it)
2849 if negative - move the end of the note earlier in time (shortening it)
2852 if (!!front_delta) {
2853 if (front_delta < 0) {
2855 if (event->note()->time() < -front_delta) {
2856 new_start = Evoral::MusicalTime();
2858 new_start = event->note()->time() + front_delta; // moves earlier
2861 /* start moved toward zero, so move the end point out to where it used to be.
2862 Note that front_delta is negative, so this increases the length.
2865 new_length = event->note()->length() - front_delta;
2866 change_start = true;
2867 change_length = true;
2871 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2873 if (new_pos < event->note()->end_time()) {
2874 new_start = event->note()->time() + front_delta;
2875 /* start moved toward the end, so move the end point back to where it used to be */
2876 new_length = event->note()->length() - front_delta;
2877 change_start = true;
2878 change_length = true;
2885 bool can_change = true;
2886 if (end_delta < 0) {
2887 if (event->note()->length() < -end_delta) {
2893 new_length = event->note()->length() + end_delta;
2894 change_length = true;
2899 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2902 if (change_length) {
2903 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2908 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2910 uint8_t new_channel;
2914 if (event->note()->channel() < -chn) {
2917 new_channel = event->note()->channel() + chn;
2920 new_channel = event->note()->channel() + chn;
2923 new_channel = (uint8_t) chn;
2926 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2930 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2932 Evoral::MusicalTime new_time;
2936 if (event->note()->time() < -delta) {
2937 new_time = Evoral::MusicalTime();
2939 new_time = event->note()->time() + delta;
2942 new_time = event->note()->time() + delta;
2948 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2952 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2954 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2958 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2963 if (_selection.empty()) {
2978 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2979 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2985 start_note_diff_command (_("change velocities"));
2987 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2988 Selection::iterator next = i;
2992 if (i == _selection.begin()) {
2993 change_note_velocity (*i, delta, true);
2994 value = (*i)->note()->velocity() + delta;
2996 change_note_velocity (*i, value, false);
3000 change_note_velocity (*i, delta, true);
3009 if (!_selection.empty()) {
3011 snprintf (buf, sizeof (buf), "Vel %d",
3012 (int) (*_selection.begin())->note()->velocity());
3013 show_verbose_cursor (buf, 10, 10);
3019 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3021 if (_selection.empty()) {
3038 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3040 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3044 if ((int8_t) (*i)->note()->note() + delta > 127) {
3051 start_note_diff_command (_("transpose"));
3053 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3054 Selection::iterator next = i;
3056 change_note_note (*i, delta, true);
3064 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3068 delta = Evoral::MusicalTime(1.0/128.0);
3070 /* grab the current grid distance */
3071 delta = get_grid_beats(_region->position());
3079 start_note_diff_command (_("change note lengths"));
3081 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3082 Selection::iterator next = i;
3085 /* note the negation of the delta for start */
3088 (start ? -delta : Evoral::MusicalTime()),
3089 (end ? delta : Evoral::MusicalTime()));
3098 MidiRegionView::nudge_notes (bool forward, bool fine)
3100 if (_selection.empty()) {
3104 /* pick a note as the point along the timeline to get the nudge distance.
3105 its not necessarily the earliest note, so we may want to pull the notes out
3106 into a vector and sort before using the first one.
3109 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3110 Evoral::MusicalTime delta;
3114 /* non-fine, move by 1 bar regardless of snap */
3115 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3117 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3119 /* grid is off - use nudge distance */
3122 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3123 delta = region_frames_to_region_beats (fabs ((double)distance));
3129 framepos_t next_pos = ref_point;
3132 if (max_framepos - 1 < next_pos) {
3136 if (next_pos == 0) {
3142 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3143 const framecnt_t distance = ref_point - next_pos;
3144 delta = region_frames_to_region_beats (fabs ((double)distance));
3155 start_note_diff_command (_("nudge"));
3157 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3158 Selection::iterator next = i;
3160 change_note_time (*i, delta, true);
3168 MidiRegionView::change_channel(uint8_t channel)
3170 start_note_diff_command(_("change channel"));
3171 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3172 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3180 MidiRegionView::note_entered(NoteBase* ev)
3182 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3184 pre_note_enter_cursor = editor->get_canvas_cursor ();
3186 if (_mouse_state == SelectTouchDragging) {
3187 note_selected (ev, true);
3190 show_verbose_cursor (ev->note ());
3194 MidiRegionView::note_left (NoteBase*)
3196 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3198 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3199 (*i)->hide_velocity ();
3202 editor->verbose_cursor()->hide ();
3204 if (pre_note_enter_cursor) {
3205 editor->set_canvas_cursor (pre_note_enter_cursor);
3206 pre_note_enter_cursor = 0;
3211 MidiRegionView::patch_entered (PatchChange* p)
3214 /* XXX should get patch name if we can */
3215 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3216 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3217 << _("Channel ") << ((int) p->patch()->channel() + 1);
3218 show_verbose_cursor (s.str(), 10, 20);
3219 p->item().grab_focus();
3223 MidiRegionView::patch_left (PatchChange *)
3225 trackview.editor().verbose_cursor()->hide ();
3226 /* focus will transfer back via the enter-notify event sent to this
3232 MidiRegionView::sysex_entered (SysEx* p)
3236 // need a way to extract text from p->_flag->_text
3238 // show_verbose_cursor (s.str(), 10, 20);
3239 p->item().grab_focus();
3243 MidiRegionView::sysex_left (SysEx *)
3245 trackview.editor().verbose_cursor()->hide ();
3246 /* focus will transfer back via the enter-notify event sent to this
3252 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3254 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3255 Editing::MouseMode mm = editor->current_mouse_mode();
3256 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3258 if (can_set_cursor) {
3259 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3260 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3261 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3262 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3263 } else if (pre_note_enter_cursor) {
3264 editor->set_canvas_cursor (pre_note_enter_cursor);
3270 MidiRegionView::set_frame_color()
3274 TimeAxisViewItem::set_frame_color ();
3281 f = ARDOUR_UI::config()->get_SelectedFrameBase();
3282 } else if (high_enough_for_name) {
3283 f= ARDOUR_UI::config()->get_MidiFrameBase();
3288 if (!rect_visible) {
3289 f = UINT_RGBA_CHANGE_A (f, 80);
3292 frame->set_fill_color (f);
3296 MidiRegionView::midi_channel_mode_changed ()
3298 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3299 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3300 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3302 if (mode == ForceChannel) {
3303 mask = 0xFFFF; // Show all notes as active (below)
3306 // Update notes for selection
3307 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3308 (*i)->on_channel_selection_change (mask);
3311 _patch_changes.clear ();
3312 display_patch_changes ();
3316 MidiRegionView::instrument_settings_changed ()
3322 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3324 if (_selection.empty()) {
3328 PublicEditor& editor (trackview.editor());
3332 /* XXX what to do ? */
3336 editor.get_cut_buffer().add (selection_as_cut_buffer());
3344 start_note_diff_command();
3346 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3353 note_diff_remove_note (*i);
3363 MidiRegionView::selection_as_cut_buffer () const
3367 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3368 NoteType* n = (*i)->note().get();
3369 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3372 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3378 /** This method handles undo */
3380 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3382 trackview.session()->begin_reversible_command (Operations::paste);
3384 // Paste notes, if available
3385 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3386 if (m != selection.midi_notes.end()) {
3387 ctx.counts.increase_n_notes();
3388 paste_internal(pos, ctx.count, ctx.times, **m);
3391 // Paste control points to automation children, if available
3392 typedef RouteTimeAxisView::AutomationTracks ATracks;
3393 const ATracks& atracks = midi_view()->automation_tracks();
3394 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3395 a->second->paste(pos, selection, ctx);
3398 trackview.session()->commit_reversible_command ();
3403 /** This method handles undo */
3405 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3411 start_note_diff_command (_("paste"));
3413 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3414 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3415 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3416 const Evoral::MusicalTime duration = last_time - first_time;
3417 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3418 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3419 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3420 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3422 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3425 duration, pos, _region->position(),
3430 for (int n = 0; n < (int) times; ++n) {
3432 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3434 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3435 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3437 /* make all newly added notes selected */
3439 note_diff_add_note (copied_note, true);
3440 end_point = copied_note->end_time();
3444 /* if we pasted past the current end of the region, extend the region */
3446 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3447 framepos_t region_end = _region->position() + _region->length() - 1;
3449 if (end_frame > region_end) {
3451 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3453 _region->clear_changes ();
3454 _region->set_length (end_frame - _region->position());
3455 trackview.session()->add_command (new StatefulDiffCommand (_region));
3461 struct EventNoteTimeEarlyFirstComparator {
3462 bool operator() (NoteBase* a, NoteBase* b) {
3463 return a->note()->time() < b->note()->time();
3468 MidiRegionView::time_sort_events ()
3470 if (!_sort_needed) {
3474 EventNoteTimeEarlyFirstComparator cmp;
3477 _sort_needed = false;
3481 MidiRegionView::goto_next_note (bool add_to_selection)
3483 bool use_next = false;
3485 if (_events.back()->selected()) {
3489 time_sort_events ();
3491 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3492 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3494 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3495 if ((*i)->selected()) {
3498 } else if (use_next) {
3499 if (channel_mask & (1 << (*i)->note()->channel())) {
3500 if (!add_to_selection) {
3503 note_selected (*i, true, false);
3510 /* use the first one */
3512 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3513 unique_select (_events.front());
3518 MidiRegionView::goto_previous_note (bool add_to_selection)
3520 bool use_next = false;
3522 if (_events.front()->selected()) {
3526 time_sort_events ();
3528 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3529 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3531 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3532 if ((*i)->selected()) {
3535 } else if (use_next) {
3536 if (channel_mask & (1 << (*i)->note()->channel())) {
3537 if (!add_to_selection) {
3540 note_selected (*i, true, false);
3547 /* use the last one */
3549 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3550 unique_select (*(_events.rbegin()));
3555 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3557 bool had_selected = false;
3559 time_sort_events ();
3561 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3562 if ((*i)->selected()) {
3563 selected.insert ((*i)->note());
3564 had_selected = true;
3568 if (allow_all_if_none_selected && !had_selected) {
3569 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3570 selected.insert ((*i)->note());
3576 MidiRegionView::update_ghost_note (double x, double y)
3578 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3583 _note_group->canvas_to_item (x, y);
3585 PublicEditor& editor = trackview.editor ();
3587 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3588 framecnt_t grid_frames;
3589 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3591 /* use region_frames... because we are converting a delta within the region
3594 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3596 /* note that this sets the time of the ghost note in beats relative to
3597 the start of the source; that is how all note times are stored.
3599 _ghost_note->note()->set_time (
3600 std::max(Evoral::MusicalTime(),
3601 absolute_frames_to_source_beats (f + _region->position ())));
3602 _ghost_note->note()->set_length (length);
3603 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3604 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3606 /* the ghost note does not appear in ghost regions, so pass false in here */
3607 update_note (_ghost_note, false);
3609 show_verbose_cursor (_ghost_note->note ());
3613 MidiRegionView::create_ghost_note (double x, double y)
3615 remove_ghost_note ();
3617 boost::shared_ptr<NoteType> g (new NoteType);
3618 _ghost_note = new Note (*this, _note_group, g);
3619 _ghost_note->set_ignore_events (true);
3620 _ghost_note->set_outline_color (0x000000aa);
3621 if (x < 0) { x = 0; }
3622 update_ghost_note (x, y);
3623 _ghost_note->show ();
3628 show_verbose_cursor (_ghost_note->note ());
3632 MidiRegionView::remove_ghost_note ()
3639 MidiRegionView::snap_changed ()
3645 create_ghost_note (_last_ghost_x, _last_ghost_y);
3649 MidiRegionView::drop_down_keys ()
3651 _mouse_state = None;
3655 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3657 /* XXX: This is dead code. What was it for? */
3659 double note = midi_stream_view()->y_to_note(y);
3661 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3663 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3665 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3666 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3667 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3668 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3673 bool add_mrv_selection = false;
3675 if (_selection.empty()) {
3676 add_mrv_selection = true;
3679 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3680 if (_selection.insert (*i).second) {
3681 (*i)->set_selected (true);
3685 if (add_mrv_selection) {
3686 PublicEditor& editor (trackview.editor());
3687 editor.get_selection().add (this);
3692 MidiRegionView::color_handler ()
3694 RegionView::color_handler ();
3696 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3697 (*i)->set_selected ((*i)->selected()); // will change color
3700 /* XXX probably more to do here */
3704 MidiRegionView::enable_display (bool yn)
3706 RegionView::enable_display (yn);
3713 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3715 if (_step_edit_cursor == 0) {
3716 ArdourCanvas::Item* const group = get_canvas_group();
3718 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3719 _step_edit_cursor->set_y0 (0);
3720 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3721 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3722 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3725 move_step_edit_cursor (pos);
3726 _step_edit_cursor->show ();
3730 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3732 _step_edit_cursor_position = pos;
3734 if (_step_edit_cursor) {
3735 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3736 _step_edit_cursor->set_x0 (pixel);
3737 set_step_edit_cursor_width (_step_edit_cursor_width);
3742 MidiRegionView::hide_step_edit_cursor ()
3744 if (_step_edit_cursor) {
3745 _step_edit_cursor->hide ();
3750 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3752 _step_edit_cursor_width = beats;
3754 if (_step_edit_cursor) {
3755 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3759 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3760 * @param w Source that the data will end up in.
3763 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3765 if (!_active_notes) {
3766 /* we aren't actively being recorded to */
3770 boost::shared_ptr<MidiSource> src = w.lock ();
3771 if (!src || src != midi_region()->midi_source()) {
3772 /* recorded data was not destined for our source */
3776 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3778 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3780 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3782 framepos_t back = max_framepos;
3784 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3785 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3787 if (ev.is_channel_event()) {
3788 if (get_channel_mode() == FilterChannels) {
3789 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3795 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3796 frames from the start of the source, and so time_beats is in terms of the
3800 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3802 if (ev.type() == MIDI_CMD_NOTE_ON) {
3803 boost::shared_ptr<NoteType> note (
3804 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3806 add_note (note, true);
3808 /* fix up our note range */
3809 if (ev.note() < _current_range_min) {
3810 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3811 } else if (ev.note() > _current_range_max) {
3812 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3815 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3816 resolve_note (ev.note (), time_beats);
3822 midi_stream_view()->check_record_layers (region(), back);
3826 MidiRegionView::trim_front_starting ()
3828 /* Reparent the note group to the region view's parent, so that it doesn't change
3829 when the region view is trimmed.
3831 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3832 _temporary_note_group->move (group->position ());
3833 _note_group->reparent (_temporary_note_group);
3837 MidiRegionView::trim_front_ending ()
3839 _note_group->reparent (group);
3840 delete _temporary_note_group;
3841 _temporary_note_group = 0;
3843 if (_region->start() < 0) {
3844 /* Trim drag made start time -ve; fix this */
3845 midi_region()->fix_negative_start ();
3850 MidiRegionView::edit_patch_change (PatchChange* pc)
3852 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3854 int response = d.run();
3857 case Gtk::RESPONSE_ACCEPT:
3859 case Gtk::RESPONSE_REJECT:
3860 delete_patch_change (pc);
3866 change_patch_change (pc->patch(), d.patch ());
3870 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3873 // sysyex object doesn't have a pointer to a sysex event
3874 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3875 // c->remove (sysex->sysex());
3876 // _model->apply_command (*trackview.session(), c);
3878 //_sys_exes.clear ();
3879 // display_sysexes();
3883 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3885 using namespace MIDI::Name;
3889 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3891 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3893 MIDI::Name::PatchPrimaryKey patch_key;
3894 get_patch_key_at(n->time(), n->channel(), patch_key);
3895 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3897 patch_key.bank_number,
3898 patch_key.program_number,
3904 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3906 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3907 (int) n->channel() + 1,
3908 (int) n->velocity());
3910 show_verbose_cursor(buf, 10, 20);
3914 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3916 trackview.editor().verbose_cursor()->set (text);
3917 trackview.editor().verbose_cursor()->show ();
3918 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3921 /** @param p A session framepos.
3922 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3923 * @return p snapped to the grid subdivision underneath it.
3926 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3928 PublicEditor& editor = trackview.editor ();
3930 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3932 grid_frames = region_beats_to_region_frames (grid_beats);
3934 /* Hack so that we always snap to the note that we are over, instead of snapping
3935 to the next one if we're more than halfway through the one we're over.
3937 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3938 p -= grid_frames / 2;
3941 return snap_frame_to_frame (p);
3944 /** Called when the selection has been cleared in any MidiRegionView.
3945 * @param rv MidiRegionView that the selection was cleared in.
3948 MidiRegionView::selection_cleared (MidiRegionView* rv)
3954 /* Clear our selection in sympathy; but don't signal the fact */
3955 clear_selection (false);
3959 MidiRegionView::note_button_release ()
3961 delete _note_player;
3966 MidiRegionView::get_channel_mode () const
3968 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3969 return rtav->midi_track()->get_playback_channel_mode();
3973 MidiRegionView::get_selected_channels () const
3975 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3976 return rtav->midi_track()->get_playback_channel_mask();
3981 MidiRegionView::get_grid_beats(framepos_t pos) const
3983 PublicEditor& editor = trackview.editor();
3984 bool success = false;
3985 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3987 beats = Evoral::MusicalTime(1);