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 && unmodified) {
794 } else if (ev->keyval == GDK_Right && unmodified) {
799 } else if (ev->keyval == GDK_c && unmodified) {
803 } else if (ev->keyval == GDK_v && unmodified) {
812 MidiRegionView::key_release (GdkEventKey* ev)
814 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
822 MidiRegionView::channel_edit ()
824 if (_selection.empty()) {
828 /* pick a note somewhat at random (since Selection is a set<>) to
829 * provide the "current" channel for the dialog.
832 uint8_t current_channel = (*_selection.begin())->note()->channel ();
833 MidiChannelDialog channel_dialog (current_channel);
834 int ret = channel_dialog.run ();
837 case Gtk::RESPONSE_OK:
843 uint8_t new_channel = channel_dialog.active_channel ();
845 start_note_diff_command (_("channel edit"));
847 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
848 Selection::iterator next = i;
850 change_note_channel (*i, new_channel);
858 MidiRegionView::velocity_edit ()
860 if (_selection.empty()) {
864 /* pick a note somewhat at random (since Selection is a set<>) to
865 * provide the "current" velocity for the dialog.
868 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
869 MidiVelocityDialog velocity_dialog (current_velocity);
870 int ret = velocity_dialog.run ();
873 case Gtk::RESPONSE_OK:
879 uint8_t new_velocity = velocity_dialog.velocity ();
881 start_note_diff_command (_("velocity edit"));
883 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
884 Selection::iterator next = i;
886 change_note_velocity (*i, new_velocity, false);
894 MidiRegionView::show_list_editor ()
897 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
899 _list_editor->present ();
902 /** Add a note to the model, and the view, at a canvas (click) coordinate.
903 * \param t time in frames relative to the position of the region
904 * \param y vertical position in pixels
905 * \param length duration of the note in beats
906 * \param snap_t true to snap t to the grid, otherwise false.
909 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
911 if (length < 2 * DBL_EPSILON) {
915 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
916 MidiStreamView* const view = mtv->midi_view();
918 const double note = view->y_to_note(y);
920 // Start of note in frames relative to region start
922 framecnt_t grid_frames;
923 t = snap_frame_to_grid_underneath (t, grid_frames);
926 const boost::shared_ptr<NoteType> new_note (
927 new NoteType (mtv->get_channel_for_add (),
928 region_frames_to_region_beats(t + _region->start()),
930 (uint8_t)note, 0x40));
932 if (_model->contains (new_note)) {
936 view->update_note_range(new_note->note());
938 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
940 _model->apply_command(*trackview.session(), cmd);
942 play_midi_note (new_note);
946 MidiRegionView::clear_events (bool with_selection_signal)
948 clear_selection (with_selection_signal);
951 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
952 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
957 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
962 _patch_changes.clear();
964 _optimization_iterator = _events.end();
968 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
972 content_connection.disconnect ();
973 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
977 if (_enable_display) {
983 MidiRegionView::start_note_diff_command (string name)
985 if (!_note_diff_command) {
986 _note_diff_command = _model->new_note_diff_command (name);
991 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
993 if (_note_diff_command) {
994 _note_diff_command->add (note);
997 _marked_for_selection.insert(note);
1000 _marked_for_velocity.insert(note);
1005 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1007 if (_note_diff_command && ev->note()) {
1008 _note_diff_command->remove(ev->note());
1013 MidiRegionView::note_diff_add_change (NoteBase* ev,
1014 MidiModel::NoteDiffCommand::Property property,
1017 if (_note_diff_command) {
1018 _note_diff_command->change (ev->note(), property, val);
1023 MidiRegionView::note_diff_add_change (NoteBase* ev,
1024 MidiModel::NoteDiffCommand::Property property,
1025 Evoral::MusicalTime val)
1027 if (_note_diff_command) {
1028 _note_diff_command->change (ev->note(), property, val);
1033 MidiRegionView::apply_diff (bool as_subcommand)
1037 if (!_note_diff_command) {
1041 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1042 // Mark all selected notes for selection when model reloads
1043 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1044 _marked_for_selection.insert((*i)->note());
1048 if (as_subcommand) {
1049 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1051 _model->apply_command (*trackview.session(), _note_diff_command);
1054 _note_diff_command = 0;
1055 midi_view()->midi_track()->playlist_modified();
1057 if (add_or_remove) {
1058 _marked_for_selection.clear();
1061 _marked_for_velocity.clear();
1065 MidiRegionView::abort_command()
1067 delete _note_diff_command;
1068 _note_diff_command = 0;
1073 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1075 if (_optimization_iterator != _events.end()) {
1076 ++_optimization_iterator;
1079 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1080 return *_optimization_iterator;
1083 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1084 if ((*_optimization_iterator)->note() == note) {
1085 return *_optimization_iterator;
1093 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1095 MidiModel::Notes notes;
1096 _model->get_notes (notes, op, val, chan_mask);
1098 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1099 NoteBase* cne = find_canvas_note (*n);
1107 MidiRegionView::redisplay_model()
1109 // Don't redisplay the model if we're currently recording and displaying that
1110 if (_active_notes) {
1118 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1119 (*i)->invalidate ();
1122 MidiModel::ReadLock lock(_model->read_lock());
1124 MidiModel::Notes& notes (_model->notes());
1125 _optimization_iterator = _events.begin();
1127 bool empty_when_starting = _events.empty();
1129 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1131 boost::shared_ptr<NoteType> note (*n);
1135 if (note_in_region_range (note, visible)) {
1137 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1144 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1146 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1158 add_note (note, visible);
1163 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1171 /* remove note items that are no longer valid */
1173 if (!empty_when_starting) {
1174 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1175 if (!(*i)->valid ()) {
1177 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1178 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1180 gr->remove_note (*i);
1185 i = _events.erase (i);
1193 _patch_changes.clear();
1197 display_patch_changes ();
1199 _marked_for_selection.clear ();
1200 _marked_for_velocity.clear ();
1202 /* we may have caused _events to contain things out of order (e.g. if a note
1203 moved earlier or later). we don't generally need them in time order, but
1204 make a note that a sort is required for those cases that require it.
1207 _sort_needed = true;
1211 MidiRegionView::display_patch_changes ()
1213 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1214 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1216 for (uint8_t i = 0; i < 16; ++i) {
1217 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1221 /** @param active_channel true to display patch changes fully, false to display
1222 * them `greyed-out' (as on an inactive channel)
1225 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1227 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1229 if ((*i)->channel() != channel) {
1233 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1234 add_canvas_patch_change (*i, patch_name, active_channel);
1239 MidiRegionView::display_sysexes()
1241 bool have_periodic_system_messages = false;
1242 bool display_periodic_messages = true;
1244 if (!Config->get_never_display_periodic_midi()) {
1246 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1247 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1248 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1251 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1252 have_periodic_system_messages = true;
1258 if (have_periodic_system_messages) {
1259 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1261 /* get an approximate value for the number of samples per video frame */
1263 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1265 /* if we are zoomed out beyond than the cutoff (i.e. more
1266 * frames per pixel than frames per 4 video frames), don't
1267 * show periodic sysex messages.
1270 if (zoom > (video_frame*4)) {
1271 display_periodic_messages = false;
1275 display_periodic_messages = false;
1278 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1280 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1281 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1283 Evoral::MusicalTime time = (*i)->time();
1286 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1287 if (!display_periodic_messages) {
1295 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1296 str << int((*i)->buffer()[b]);
1297 if (b != (*i)->size() -1) {
1301 string text = str.str();
1303 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1305 double height = midi_stream_view()->contents_height();
1307 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1308 // SysEx canvas object!!!
1310 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1311 new SysEx (*this, _note_group, text, height, x, 1.0));
1313 // Show unless message is beyond the region bounds
1314 if (time - _region->start() >= _region->length() || time < _region->start()) {
1320 _sys_exes.push_back(sysex);
1324 MidiRegionView::~MidiRegionView ()
1326 in_destructor = true;
1328 trackview.editor().verbose_cursor()->hide ();
1330 note_delete_connection.disconnect ();
1332 delete _list_editor;
1334 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1336 if (_active_notes) {
1340 _selection_cleared_connection.disconnect ();
1343 clear_events (false);
1346 delete _note_diff_command;
1347 delete _step_edit_cursor;
1348 delete _temporary_note_group;
1352 MidiRegionView::region_resized (const PropertyChange& what_changed)
1354 RegionView::region_resized(what_changed);
1356 if (what_changed.contains (ARDOUR::Properties::position)) {
1357 _region_relative_time_converter.set_origin_b(_region->position());
1358 set_duration(_region->length(), 0);
1359 if (_enable_display) {
1364 if (what_changed.contains (ARDOUR::Properties::start) ||
1365 what_changed.contains (ARDOUR::Properties::position)) {
1366 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1371 MidiRegionView::reset_width_dependent_items (double pixel_width)
1373 RegionView::reset_width_dependent_items(pixel_width);
1375 if (_enable_display) {
1379 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1380 if ((*x)->canvas_item()->width() >= _pixel_width) {
1387 move_step_edit_cursor (_step_edit_cursor_position);
1388 set_step_edit_cursor_width (_step_edit_cursor_width);
1392 MidiRegionView::set_height (double height)
1394 double old_height = _height;
1395 RegionView::set_height(height);
1397 apply_note_range (midi_stream_view()->lowest_note(),
1398 midi_stream_view()->highest_note(),
1399 height != old_height);
1402 name_text->raise_to_top();
1405 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1406 (*x)->set_height (midi_stream_view()->contents_height());
1409 if (_step_edit_cursor) {
1410 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1415 /** Apply the current note range from the stream view
1416 * by repositioning/hiding notes as necessary
1419 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1421 if (!_enable_display) {
1425 if (!force && _current_range_min == min && _current_range_max == max) {
1429 _current_range_min = min;
1430 _current_range_max = max;
1432 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1433 NoteBase* event = *i;
1434 boost::shared_ptr<NoteType> note (event->note());
1436 if (note->note() < _current_range_min ||
1437 note->note() > _current_range_max) {
1443 if (Note* cnote = dynamic_cast<Note*>(event)) {
1445 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1446 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1451 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1458 MidiRegionView::add_ghost (TimeAxisView& tv)
1462 double unit_position = _region->position () / samples_per_pixel;
1463 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1464 MidiGhostRegion* ghost;
1466 if (mtv && mtv->midi_view()) {
1467 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1468 to allow having midi notes on top of note lines and waveforms.
1470 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1472 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1475 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1476 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1477 ghost->add_note(note);
1481 ghost->set_height ();
1482 ghost->set_duration (_region->length() / samples_per_pixel);
1483 ghosts.push_back (ghost);
1485 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1491 /** Begin tracking note state for successive calls to add_event
1494 MidiRegionView::begin_write()
1496 if (_active_notes) {
1497 delete[] _active_notes;
1499 _active_notes = new Note*[128];
1500 for (unsigned i = 0; i < 128; ++i) {
1501 _active_notes[i] = 0;
1506 /** Destroy note state for add_event
1509 MidiRegionView::end_write()
1511 delete[] _active_notes;
1513 _marked_for_selection.clear();
1514 _marked_for_velocity.clear();
1518 /** Resolve an active MIDI note (while recording).
1521 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1523 if (midi_view()->note_mode() != Sustained) {
1527 if (_active_notes && _active_notes[note]) {
1529 /* XXX is end_time really region-centric? I think so, because
1530 this is a new region that we're recording, so source zero is
1531 the same as region zero
1533 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1535 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1536 _active_notes[note]->set_outline_all ();
1537 _active_notes[note] = 0;
1543 /** Extend active notes to rightmost edge of region (if length is changed)
1546 MidiRegionView::extend_active_notes()
1548 if (!_active_notes) {
1552 for (unsigned i=0; i < 128; ++i) {
1553 if (_active_notes[i]) {
1554 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1561 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1563 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1567 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1569 if (!route_ui || !route_ui->midi_track()) {
1573 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1577 /* NotePlayer deletes itself */
1581 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1583 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1587 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1589 if (!route_ui || !route_ui->midi_track()) {
1593 delete _note_player;
1594 _note_player = new NotePlayer (route_ui->midi_track ());
1595 _note_player->add (note);
1596 _note_player->on ();
1600 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1602 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1606 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1608 if (!route_ui || !route_ui->midi_track()) {
1612 delete _note_player;
1613 _note_player = new NotePlayer (route_ui->midi_track());
1615 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1616 _note_player->add (*n);
1619 _note_player->on ();
1624 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1626 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1627 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1629 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1630 (note->note() <= midi_stream_view()->highest_note());
1635 /** Update a canvas note's size from its model note.
1636 * @param ev Canvas note to update.
1637 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1640 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1642 boost::shared_ptr<NoteType> note = ev->note();
1643 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1644 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1649 /* trim note display to not overlap the end of its region */
1651 if (note->length() > 0) {
1652 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1653 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1655 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1658 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1660 if (!note->length()) {
1661 if (_active_notes && note->note() < 128) {
1662 // If this note is already active there's a stuck note,
1663 // finish the old note rectangle
1664 if (_active_notes[note->note()]) {
1665 Note* const old_rect = _active_notes[note->note()];
1666 boost::shared_ptr<NoteType> old_note = old_rect->note();
1667 old_rect->set_x1 (x);
1668 old_rect->set_outline_all ();
1670 _active_notes[note->note()] = ev;
1672 /* outline all but right edge */
1673 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1674 ArdourCanvas::Rectangle::TOP|
1675 ArdourCanvas::Rectangle::LEFT|
1676 ArdourCanvas::Rectangle::BOTTOM));
1678 /* outline all edges */
1679 ev->set_outline_all ();
1682 if (update_ghost_regions) {
1683 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1684 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1686 gr->update_note (ev);
1693 MidiRegionView::update_hit (Hit* ev)
1695 boost::shared_ptr<NoteType> note = ev->note();
1697 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1698 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1699 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1700 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1702 ev->set_position (ArdourCanvas::Duple (x, y));
1703 ev->set_height (diamond_size);
1706 /** Add a MIDI note to the view (with length).
1708 * If in sustained mode, notes with length 0 will be considered active
1709 * notes, and resolve_note should be called when the corresponding note off
1710 * event arrives, to properly display the note.
1713 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1715 NoteBase* event = 0;
1717 if (midi_view()->note_mode() == Sustained) {
1719 Note* ev_rect = new Note (*this, _note_group, note);
1721 update_note (ev_rect);
1725 MidiGhostRegion* gr;
1727 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1728 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1729 gr->add_note(ev_rect);
1733 } else if (midi_view()->note_mode() == Percussive) {
1735 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1737 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1739 update_hit (ev_diamond);
1748 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1749 note_selected(event, true);
1752 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1753 event->show_velocity();
1756 event->on_channel_selection_change (get_selected_channels());
1757 _events.push_back(event);
1766 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1767 MidiStreamView* const view = mtv->midi_view();
1769 view->update_note_range (note->note());
1773 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1774 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1776 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1778 /* potentially extend region to hold new note */
1780 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1781 framepos_t region_end = _region->last_frame();
1783 if (end_frame > region_end) {
1784 _region->set_length (end_frame - _region->position());
1787 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1788 MidiStreamView* const view = mtv->midi_view();
1790 view->update_note_range(new_note->note());
1792 _marked_for_selection.clear ();
1795 start_note_diff_command (_("step add"));
1796 note_diff_add_note (new_note, true, false);
1799 // last_step_edit_note = new_note;
1803 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1805 change_note_lengths (false, false, beats, false, true);
1808 /** Add a new patch change flag to the canvas.
1809 * @param patch the patch change to add
1810 * @param the text to display in the flag
1811 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1814 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1816 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1817 const double x = trackview.editor().sample_to_pixel (region_frames);
1819 double const height = midi_stream_view()->contents_height();
1821 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1822 // so we need to do something more sophisticated to keep its color
1823 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1826 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1827 new PatchChange(*this, group,
1834 if (patch_change->item().width() < _pixel_width) {
1835 // Show unless patch change is beyond the region bounds
1836 if (region_frames < 0 || region_frames >= _region->length()) {
1837 patch_change->hide();
1839 patch_change->show();
1842 patch_change->hide ();
1845 _patch_changes.push_back (patch_change);
1848 MIDI::Name::PatchPrimaryKey
1849 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1851 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1854 /// Return true iff @p pc applies to the given time on the given channel.
1856 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1858 return pc->time() <= time && pc->channel() == channel;
1862 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1864 // The earliest event not before time
1865 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1867 // Go backwards until we find the latest PC for this channel, or the start
1868 while (i != _model->patch_changes().begin() &&
1869 (i == _model->patch_changes().end() ||
1870 !patch_applies(*i, time, channel))) {
1874 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1875 key.bank_number = (*i)->bank();
1876 key.program_number = (*i)->program ();
1878 key.bank_number = key.program_number = 0;
1881 if (!key.is_sane()) {
1882 error << string_compose(_("insane MIDI patch key %1:%2"),
1883 key.bank_number, key.program_number) << endmsg;
1888 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1890 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1892 if (pc.patch()->program() != new_patch.program_number) {
1893 c->change_program (pc.patch (), new_patch.program_number);
1896 int const new_bank = new_patch.bank_number;
1897 if (pc.patch()->bank() != new_bank) {
1898 c->change_bank (pc.patch (), new_bank);
1901 _model->apply_command (*trackview.session(), c);
1903 _patch_changes.clear ();
1904 display_patch_changes ();
1908 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1910 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1912 if (old_change->time() != new_change.time()) {
1913 c->change_time (old_change, new_change.time());
1916 if (old_change->channel() != new_change.channel()) {
1917 c->change_channel (old_change, new_change.channel());
1920 if (old_change->program() != new_change.program()) {
1921 c->change_program (old_change, new_change.program());
1924 if (old_change->bank() != new_change.bank()) {
1925 c->change_bank (old_change, new_change.bank());
1928 _model->apply_command (*trackview.session(), c);
1930 _patch_changes.clear ();
1931 display_patch_changes ();
1934 /** Add a patch change to the region.
1935 * @param t Time in frames relative to region position
1936 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1937 * MidiTimeAxisView::get_channel_for_add())
1940 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1942 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1944 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1945 c->add (MidiModel::PatchChangePtr (
1946 new Evoral::PatchChange<Evoral::MusicalTime> (
1947 absolute_frames_to_source_beats (_region->position() + t),
1948 mtv->get_channel_for_add(), patch.program(), patch.bank()
1953 _model->apply_command (*trackview.session(), c);
1955 _patch_changes.clear ();
1956 display_patch_changes ();
1960 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1962 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1963 c->change_time (pc.patch (), t);
1964 _model->apply_command (*trackview.session(), c);
1966 _patch_changes.clear ();
1967 display_patch_changes ();
1971 MidiRegionView::delete_patch_change (PatchChange* pc)
1973 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1974 c->remove (pc->patch ());
1975 _model->apply_command (*trackview.session(), c);
1977 _patch_changes.clear ();
1978 display_patch_changes ();
1982 MidiRegionView::previous_patch (PatchChange& patch)
1984 if (patch.patch()->program() < 127) {
1985 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1986 key.program_number++;
1987 change_patch_change (patch, key);
1992 MidiRegionView::next_patch (PatchChange& patch)
1994 if (patch.patch()->program() > 0) {
1995 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1996 key.program_number--;
1997 change_patch_change (patch, key);
2002 MidiRegionView::next_bank (PatchChange& patch)
2004 if (patch.patch()->program() < 127) {
2005 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2006 if (key.bank_number > 0) {
2008 change_patch_change (patch, key);
2014 MidiRegionView::previous_bank (PatchChange& patch)
2016 if (patch.patch()->program() > 0) {
2017 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2018 if (key.bank_number < 127) {
2020 change_patch_change (patch, key);
2026 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2028 if (_selection.empty()) {
2032 _selection.erase (cne);
2036 MidiRegionView::delete_selection()
2038 if (_selection.empty()) {
2042 start_note_diff_command (_("delete selection"));
2044 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2045 if ((*i)->selected()) {
2046 _note_diff_command->remove((*i)->note());
2056 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2058 start_note_diff_command (_("delete note"));
2059 _note_diff_command->remove (n);
2062 trackview.editor().verbose_cursor()->hide ();
2066 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2068 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2070 Selection::iterator tmp = i;
2073 (*i)->set_selected (false);
2074 (*i)->hide_velocity ();
2075 _selection.erase (i);
2083 if (!ev && _entered) {
2084 // Clearing selection entirely, ungrab keyboard
2085 Keyboard::magic_widget_drop_focus();
2086 _grabbed_keyboard = false;
2089 /* this does not change the status of this regionview w.r.t the editor
2094 SelectionCleared (this); /* EMIT SIGNAL */
2099 MidiRegionView::unique_select(NoteBase* ev)
2101 const bool selection_was_empty = _selection.empty();
2103 clear_selection_except (ev);
2105 /* don't bother with checking to see if we should remove this
2106 regionview from the editor selection, since we're about to add
2107 another note, and thus put/keep this regionview in the editor
2111 if (!ev->selected()) {
2112 add_to_selection (ev);
2113 if (selection_was_empty && _entered) {
2114 // Grab keyboard for moving notes with arrow keys
2115 Keyboard::magic_widget_grab_focus();
2116 _grabbed_keyboard = true;
2122 MidiRegionView::select_all_notes ()
2126 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2127 add_to_selection (*i);
2132 MidiRegionView::select_range (framepos_t start, framepos_t end)
2136 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2137 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2138 if (t >= start && t <= end) {
2139 add_to_selection (*i);
2145 MidiRegionView::invert_selection ()
2147 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2148 if ((*i)->selected()) {
2149 remove_from_selection(*i);
2151 add_to_selection (*i);
2157 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2159 bool have_selection = !_selection.empty();
2160 uint8_t low_note = 127;
2161 uint8_t high_note = 0;
2162 MidiModel::Notes& notes (_model->notes());
2163 _optimization_iterator = _events.begin();
2165 if (extend && !have_selection) {
2169 /* scan existing selection to get note range */
2171 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2172 if ((*i)->note()->note() < low_note) {
2173 low_note = (*i)->note()->note();
2175 if ((*i)->note()->note() > high_note) {
2176 high_note = (*i)->note()->note();
2183 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2184 /* only note previously selected is the one we are
2185 * reselecting. treat this as cancelling the selection.
2192 low_note = min (low_note, notenum);
2193 high_note = max (high_note, notenum);
2196 _no_sound_notes = true;
2198 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2200 boost::shared_ptr<NoteType> note (*n);
2202 bool select = false;
2204 if (((1 << note->channel()) & channel_mask) != 0) {
2206 if ((note->note() >= low_note && note->note() <= high_note)) {
2209 } else if (note->note() == notenum) {
2215 if ((cne = find_canvas_note (note)) != 0) {
2216 // extend is false because we've taken care of it,
2217 // since it extends by time range, not pitch.
2218 note_selected (cne, add, false);
2222 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2226 _no_sound_notes = false;
2230 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2232 MidiModel::Notes& notes (_model->notes());
2233 _optimization_iterator = _events.begin();
2235 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2237 boost::shared_ptr<NoteType> note (*n);
2240 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2241 if ((cne = find_canvas_note (note)) != 0) {
2242 if (cne->selected()) {
2243 note_deselected (cne);
2245 note_selected (cne, true, false);
2253 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2256 clear_selection_except (ev);
2257 if (!_selection.empty()) {
2258 PublicEditor& editor (trackview.editor());
2259 editor.get_selection().add (this);
2265 if (!ev->selected()) {
2266 add_to_selection (ev);
2270 /* find end of latest note selected, select all between that and the start of "ev" */
2272 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2273 Evoral::MusicalTime latest = Evoral::MusicalTime();
2275 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2276 if ((*i)->note()->end_time() > latest) {
2277 latest = (*i)->note()->end_time();
2279 if ((*i)->note()->time() < earliest) {
2280 earliest = (*i)->note()->time();
2284 if (ev->note()->end_time() > latest) {
2285 latest = ev->note()->end_time();
2288 if (ev->note()->time() < earliest) {
2289 earliest = ev->note()->time();
2292 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2294 /* find notes entirely within OR spanning the earliest..latest range */
2296 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2297 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2298 add_to_selection (*i);
2306 MidiRegionView::note_deselected(NoteBase* ev)
2308 remove_from_selection (ev);
2312 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2314 PublicEditor& editor = trackview.editor();
2316 // Convert to local coordinates
2317 const framepos_t p = _region->position();
2318 const double y = midi_view()->y_position();
2319 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2320 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2321 const double y0 = max(0.0, gy0 - y);
2322 const double y1 = max(0.0, gy1 - y);
2324 // TODO: Make this faster by storing the last updated selection rect, and only
2325 // adjusting things that are in the area that appears/disappeared.
2326 // We probably need a tree to be able to find events in O(log(n)) time.
2328 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2329 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2330 // Rectangles intersect
2331 if (!(*i)->selected()) {
2332 add_to_selection (*i);
2334 } else if ((*i)->selected() && !extend) {
2335 // Rectangles do not intersect
2336 remove_from_selection (*i);
2340 typedef RouteTimeAxisView::AutomationTracks ATracks;
2341 typedef std::list<Selectable*> Selectables;
2343 /* Add control points to selection. */
2344 const ATracks& atracks = midi_view()->automation_tracks();
2345 Selectables selectables;
2346 editor.get_selection().clear_points();
2347 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2348 a->second->get_selectables(start, end, gy0, gy1, selectables);
2349 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2350 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2352 editor.get_selection().add(cp);
2355 a->second->set_selected_points(editor.get_selection().points);
2360 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2366 // TODO: Make this faster by storing the last updated selection rect, and only
2367 // adjusting things that are in the area that appears/disappeared.
2368 // We probably need a tree to be able to find events in O(log(n)) time.
2370 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2371 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2372 // within y- (note-) range
2373 if (!(*i)->selected()) {
2374 add_to_selection (*i);
2376 } else if ((*i)->selected() && !extend) {
2377 remove_from_selection (*i);
2383 MidiRegionView::remove_from_selection (NoteBase* ev)
2385 Selection::iterator i = _selection.find (ev);
2387 if (i != _selection.end()) {
2388 _selection.erase (i);
2389 if (_selection.empty() && _grabbed_keyboard) {
2391 Keyboard::magic_widget_drop_focus();
2392 _grabbed_keyboard = false;
2396 ev->set_selected (false);
2397 ev->hide_velocity ();
2399 if (_selection.empty()) {
2400 PublicEditor& editor (trackview.editor());
2401 editor.get_selection().remove (this);
2406 MidiRegionView::add_to_selection (NoteBase* ev)
2408 const bool selection_was_empty = _selection.empty();
2410 if (_selection.insert (ev).second) {
2411 ev->set_selected (true);
2412 start_playing_midi_note ((ev)->note());
2413 if (selection_was_empty && _entered) {
2414 // Grab keyboard for moving notes with arrow keys
2415 Keyboard::magic_widget_grab_focus();
2416 _grabbed_keyboard = true;
2420 if (selection_was_empty) {
2421 PublicEditor& editor (trackview.editor());
2422 editor.get_selection().add (this);
2427 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2429 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2430 PossibleChord to_play;
2431 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2433 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2434 if ((*i)->note()->time() < earliest) {
2435 earliest = (*i)->note()->time();
2439 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2440 if ((*i)->note()->time() == earliest) {
2441 to_play.push_back ((*i)->note());
2443 (*i)->move_event(dx, dy);
2446 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2448 if (to_play.size() > 1) {
2450 PossibleChord shifted;
2452 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2453 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2454 moved_note->set_note (moved_note->note() + cumulative_dy);
2455 shifted.push_back (moved_note);
2458 start_playing_midi_chord (shifted);
2460 } else if (!to_play.empty()) {
2462 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2463 moved_note->set_note (moved_note->note() + cumulative_dy);
2464 start_playing_midi_note (moved_note);
2470 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2472 uint8_t lowest_note_in_selection = 127;
2473 uint8_t highest_note_in_selection = 0;
2474 uint8_t highest_note_difference = 0;
2476 // find highest and lowest notes first
2478 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2479 uint8_t pitch = (*i)->note()->note();
2480 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2481 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2485 cerr << "dnote: " << (int) dnote << endl;
2486 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2487 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2488 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2489 << int(highest_note_in_selection) << endl;
2490 cerr << "selection size: " << _selection.size() << endl;
2491 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2494 // Make sure the note pitch does not exceed the MIDI standard range
2495 if (highest_note_in_selection + dnote > 127) {
2496 highest_note_difference = highest_note_in_selection - 127;
2499 start_note_diff_command (_("move notes"));
2501 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2503 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2504 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2510 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2512 uint8_t original_pitch = (*i)->note()->note();
2513 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2515 // keep notes in standard midi range
2516 clamp_to_0_127(new_pitch);
2518 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2519 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2521 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2526 // care about notes being moved beyond the upper/lower bounds on the canvas
2527 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2528 highest_note_in_selection > midi_stream_view()->highest_note()) {
2529 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2533 /** @param x Pixel relative to the region position.
2534 * @return Snapped frame relative to the region position.
2537 MidiRegionView::snap_pixel_to_sample(double x)
2539 PublicEditor& editor (trackview.editor());
2540 return snap_frame_to_frame (editor.pixel_to_sample (x));
2543 /** @param x Pixel relative to the region position.
2544 * @return Snapped pixel relative to the region position.
2547 MidiRegionView::snap_to_pixel(double x)
2549 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2553 MidiRegionView::get_position_pixels()
2555 framepos_t region_frame = get_position();
2556 return trackview.editor().sample_to_pixel(region_frame);
2560 MidiRegionView::get_end_position_pixels()
2562 framepos_t frame = get_position() + get_duration ();
2563 return trackview.editor().sample_to_pixel(frame);
2567 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2569 /* the time converter will return the frame corresponding to `beats'
2570 relative to the start of the source. The start of the source
2571 is an implied position given by region->position - region->start
2573 const framepos_t source_start = _region->position() - _region->start();
2574 return source_start + _source_relative_time_converter.to (beats);
2578 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2580 /* the `frames' argument needs to be converted into a frame count
2581 relative to the start of the source before being passed in to the
2584 const framepos_t source_start = _region->position() - _region->start();
2585 return _source_relative_time_converter.from (frames - source_start);
2589 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2591 return _region_relative_time_converter.to(beats);
2595 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2597 return _region_relative_time_converter.from(frames);
2601 MidiRegionView::begin_resizing (bool /*at_front*/)
2603 _resize_data.clear();
2605 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2606 Note *note = dynamic_cast<Note*> (*i);
2608 // only insert CanvasNotes into the map
2610 NoteResizeData *resize_data = new NoteResizeData();
2611 resize_data->note = note;
2613 // create a new SimpleRect from the note which will be the resize preview
2614 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2615 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2617 // calculate the colors: get the color settings
2618 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2619 ARDOUR_UI::config()->get_MidiNoteSelected(),
2622 // make the resize preview notes more transparent and bright
2623 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2625 // calculate color based on note velocity
2626 resize_rect->set_fill_color (UINT_INTERPOLATE(
2627 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2631 resize_rect->set_outline_color (NoteBase::calculate_outline (
2632 ARDOUR_UI::config()->get_MidiNoteSelected()));
2634 resize_data->resize_rect = resize_rect;
2635 _resize_data.push_back(resize_data);
2640 /** Update resizing notes while user drags.
2641 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2642 * @param at_front which end of the note (true == note on, false == note off)
2643 * @param delta_x change in mouse position since the start of the drag
2644 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2645 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2646 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2647 * as the \a primary note.
2650 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2652 bool cursor_set = false;
2654 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2655 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2656 Note* canvas_note = (*i)->note;
2661 current_x = canvas_note->x0() + delta_x;
2663 current_x = primary->x0() + delta_x;
2667 current_x = canvas_note->x1() + delta_x;
2669 current_x = primary->x1() + delta_x;
2673 if (current_x < 0) {
2674 // This works even with snapping because RegionView::snap_frame_to_frame()
2675 // snaps forward if the snapped sample is before the beginning of the region
2678 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2679 current_x = trackview.editor().sample_to_pixel(_region->length());
2683 resize_rect->set_x0 (snap_to_pixel(current_x));
2684 resize_rect->set_x1 (canvas_note->x1());
2686 resize_rect->set_x1 (snap_to_pixel(current_x));
2687 resize_rect->set_x0 (canvas_note->x0());
2691 const double snapped_x = snap_pixel_to_sample (current_x);
2692 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2693 Evoral::MusicalTime len = Evoral::MusicalTime();
2696 if (beats < canvas_note->note()->end_time()) {
2697 len = canvas_note->note()->time() - beats;
2698 len += canvas_note->note()->length();
2701 if (beats >= canvas_note->note()->time()) {
2702 len = beats - canvas_note->note()->time();
2707 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2708 show_verbose_cursor (buf, 0, 0);
2717 /** Finish resizing notes when the user releases the mouse button.
2718 * Parameters the same as for \a update_resizing().
2721 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2723 start_note_diff_command (_("resize notes"));
2725 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2726 Note* canvas_note = (*i)->note;
2727 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2729 /* Get the new x position for this resize, which is in pixels relative
2730 * to the region position.
2737 current_x = canvas_note->x0() + delta_x;
2739 current_x = primary->x0() + delta_x;
2743 current_x = canvas_note->x1() + delta_x;
2745 current_x = primary->x1() + delta_x;
2749 if (current_x < 0) {
2752 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2753 current_x = trackview.editor().sample_to_pixel(_region->length());
2756 /* Convert that to a frame within the source */
2757 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2759 /* and then to beats */
2760 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2762 if (at_front && x_beats < canvas_note->note()->end_time()) {
2763 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2765 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2766 len += canvas_note->note()->length();
2769 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2774 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2777 /* XXX convert to beats */
2778 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2786 _resize_data.clear();
2791 MidiRegionView::abort_resizing ()
2793 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2794 delete (*i)->resize_rect;
2798 _resize_data.clear ();
2802 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2804 uint8_t new_velocity;
2807 new_velocity = event->note()->velocity() + velocity;
2808 clamp_to_0_127(new_velocity);
2810 new_velocity = velocity;
2813 event->set_selected (event->selected()); // change color
2815 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2819 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2824 new_note = event->note()->note() + note;
2829 clamp_to_0_127 (new_note);
2830 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2834 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2836 bool change_start = false;
2837 bool change_length = false;
2838 Evoral::MusicalTime new_start;
2839 Evoral::MusicalTime new_length;
2841 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2843 front_delta: if positive - move the start of the note later in time (shortening it)
2844 if negative - move the start of the note earlier in time (lengthening it)
2846 end_delta: if positive - move the end of the note later in time (lengthening it)
2847 if negative - move the end of the note earlier in time (shortening it)
2850 if (!!front_delta) {
2851 if (front_delta < 0) {
2853 if (event->note()->time() < -front_delta) {
2854 new_start = Evoral::MusicalTime();
2856 new_start = event->note()->time() + front_delta; // moves earlier
2859 /* start moved toward zero, so move the end point out to where it used to be.
2860 Note that front_delta is negative, so this increases the length.
2863 new_length = event->note()->length() - front_delta;
2864 change_start = true;
2865 change_length = true;
2869 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2871 if (new_pos < event->note()->end_time()) {
2872 new_start = event->note()->time() + front_delta;
2873 /* start moved toward the end, so move the end point back to where it used to be */
2874 new_length = event->note()->length() - front_delta;
2875 change_start = true;
2876 change_length = true;
2883 bool can_change = true;
2884 if (end_delta < 0) {
2885 if (event->note()->length() < -end_delta) {
2891 new_length = event->note()->length() + end_delta;
2892 change_length = true;
2897 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2900 if (change_length) {
2901 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2906 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2908 uint8_t new_channel;
2912 if (event->note()->channel() < -chn) {
2915 new_channel = event->note()->channel() + chn;
2918 new_channel = event->note()->channel() + chn;
2921 new_channel = (uint8_t) chn;
2924 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2928 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2930 Evoral::MusicalTime new_time;
2934 if (event->note()->time() < -delta) {
2935 new_time = Evoral::MusicalTime();
2937 new_time = event->note()->time() + delta;
2940 new_time = event->note()->time() + delta;
2946 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2950 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2952 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2956 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2961 if (_selection.empty()) {
2976 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2977 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2983 start_note_diff_command (_("change velocities"));
2985 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2986 Selection::iterator next = i;
2990 if (i == _selection.begin()) {
2991 change_note_velocity (*i, delta, true);
2992 value = (*i)->note()->velocity() + delta;
2994 change_note_velocity (*i, value, false);
2998 change_note_velocity (*i, delta, true);
3007 if (!_selection.empty()) {
3009 snprintf (buf, sizeof (buf), "Vel %d",
3010 (int) (*_selection.begin())->note()->velocity());
3011 show_verbose_cursor (buf, 10, 10);
3017 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3019 if (_selection.empty()) {
3036 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3038 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3042 if ((int8_t) (*i)->note()->note() + delta > 127) {
3049 start_note_diff_command (_("transpose"));
3051 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3052 Selection::iterator next = i;
3054 change_note_note (*i, delta, true);
3062 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3066 delta = Evoral::MusicalTime(1.0/128.0);
3068 /* grab the current grid distance */
3069 delta = get_grid_beats(_region->position());
3077 start_note_diff_command (_("change note lengths"));
3079 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3080 Selection::iterator next = i;
3083 /* note the negation of the delta for start */
3086 (start ? -delta : Evoral::MusicalTime()),
3087 (end ? delta : Evoral::MusicalTime()));
3096 MidiRegionView::nudge_notes (bool forward)
3098 if (_selection.empty()) {
3102 /* pick a note as the point along the timeline to get the nudge distance.
3103 its not necessarily the earliest note, so we may want to pull the notes out
3104 into a vector and sort before using the first one.
3107 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3109 framecnt_t distance;
3111 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3113 /* grid is off - use nudge distance */
3115 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3121 framepos_t next_pos = ref_point;
3124 if (max_framepos - 1 < next_pos) {
3128 if (next_pos == 0) {
3134 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3135 distance = ref_point - next_pos;
3138 if (distance == 0) {
3142 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs ((double)distance));
3148 start_note_diff_command (_("nudge"));
3150 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3151 Selection::iterator next = i;
3153 change_note_time (*i, delta, true);
3161 MidiRegionView::change_channel(uint8_t channel)
3163 start_note_diff_command(_("change channel"));
3164 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3165 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3173 MidiRegionView::note_entered(NoteBase* ev)
3175 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3177 pre_note_enter_cursor = editor->get_canvas_cursor ();
3179 if (_mouse_state == SelectTouchDragging) {
3180 note_selected (ev, true);
3183 show_verbose_cursor (ev->note ());
3187 MidiRegionView::note_left (NoteBase*)
3189 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3191 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3192 (*i)->hide_velocity ();
3195 editor->verbose_cursor()->hide ();
3197 if (pre_note_enter_cursor) {
3198 editor->set_canvas_cursor (pre_note_enter_cursor);
3199 pre_note_enter_cursor = 0;
3204 MidiRegionView::patch_entered (PatchChange* p)
3207 /* XXX should get patch name if we can */
3208 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3209 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3210 << _("Channel ") << ((int) p->patch()->channel() + 1);
3211 show_verbose_cursor (s.str(), 10, 20);
3212 p->item().grab_focus();
3216 MidiRegionView::patch_left (PatchChange *)
3218 trackview.editor().verbose_cursor()->hide ();
3219 /* focus will transfer back via the enter-notify event sent to this
3225 MidiRegionView::sysex_entered (SysEx* p)
3229 // need a way to extract text from p->_flag->_text
3231 // show_verbose_cursor (s.str(), 10, 20);
3232 p->item().grab_focus();
3236 MidiRegionView::sysex_left (SysEx *)
3238 trackview.editor().verbose_cursor()->hide ();
3239 /* focus will transfer back via the enter-notify event sent to this
3245 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3247 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3248 Editing::MouseMode mm = editor->current_mouse_mode();
3249 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3251 if (can_set_cursor) {
3252 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3253 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3254 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3255 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3256 } else if (pre_note_enter_cursor) {
3257 editor->set_canvas_cursor (pre_note_enter_cursor);
3263 MidiRegionView::set_frame_color()
3267 TimeAxisViewItem::set_frame_color ();
3274 f = ARDOUR_UI::config()->get_SelectedFrameBase();
3275 } else if (high_enough_for_name) {
3276 f= ARDOUR_UI::config()->get_MidiFrameBase();
3281 if (!rect_visible) {
3282 f = UINT_RGBA_CHANGE_A (f, 80);
3285 frame->set_fill_color (f);
3289 MidiRegionView::midi_channel_mode_changed ()
3291 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3292 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3293 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3295 if (mode == ForceChannel) {
3296 mask = 0xFFFF; // Show all notes as active (below)
3299 // Update notes for selection
3300 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3301 (*i)->on_channel_selection_change (mask);
3304 _patch_changes.clear ();
3305 display_patch_changes ();
3309 MidiRegionView::instrument_settings_changed ()
3315 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3317 if (_selection.empty()) {
3321 PublicEditor& editor (trackview.editor());
3325 /* XXX what to do ? */
3329 editor.get_cut_buffer().add (selection_as_cut_buffer());
3337 start_note_diff_command();
3339 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3346 note_diff_remove_note (*i);
3356 MidiRegionView::selection_as_cut_buffer () const
3360 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3361 NoteType* n = (*i)->note().get();
3362 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3365 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3371 /** This method handles undo */
3373 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3375 trackview.session()->begin_reversible_command (Operations::paste);
3377 // Paste notes, if available
3378 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3379 if (m != selection.midi_notes.end()) {
3380 ctx.counts.increase_n_notes();
3381 paste_internal(pos, ctx.count, ctx.times, **m);
3384 // Paste control points to automation children, if available
3385 typedef RouteTimeAxisView::AutomationTracks ATracks;
3386 const ATracks& atracks = midi_view()->automation_tracks();
3387 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3388 a->second->paste(pos, selection, ctx);
3391 trackview.session()->commit_reversible_command ();
3396 /** This method handles undo */
3398 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3404 start_note_diff_command (_("paste"));
3406 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3407 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3408 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3409 const Evoral::MusicalTime duration = last_time - first_time;
3410 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3411 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3412 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3413 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3415 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3418 duration, pos, _region->position(),
3423 for (int n = 0; n < (int) times; ++n) {
3425 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3427 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3428 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3430 /* make all newly added notes selected */
3432 note_diff_add_note (copied_note, true);
3433 end_point = copied_note->end_time();
3437 /* if we pasted past the current end of the region, extend the region */
3439 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3440 framepos_t region_end = _region->position() + _region->length() - 1;
3442 if (end_frame > region_end) {
3444 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3446 _region->clear_changes ();
3447 _region->set_length (end_frame - _region->position());
3448 trackview.session()->add_command (new StatefulDiffCommand (_region));
3454 struct EventNoteTimeEarlyFirstComparator {
3455 bool operator() (NoteBase* a, NoteBase* b) {
3456 return a->note()->time() < b->note()->time();
3461 MidiRegionView::time_sort_events ()
3463 if (!_sort_needed) {
3467 EventNoteTimeEarlyFirstComparator cmp;
3470 _sort_needed = false;
3474 MidiRegionView::goto_next_note (bool add_to_selection)
3476 bool use_next = false;
3478 if (_events.back()->selected()) {
3482 time_sort_events ();
3484 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3485 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3487 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3488 if ((*i)->selected()) {
3491 } else if (use_next) {
3492 if (channel_mask & (1 << (*i)->note()->channel())) {
3493 if (!add_to_selection) {
3496 note_selected (*i, true, false);
3503 /* use the first one */
3505 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3506 unique_select (_events.front());
3511 MidiRegionView::goto_previous_note (bool add_to_selection)
3513 bool use_next = false;
3515 if (_events.front()->selected()) {
3519 time_sort_events ();
3521 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3522 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3524 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3525 if ((*i)->selected()) {
3528 } else if (use_next) {
3529 if (channel_mask & (1 << (*i)->note()->channel())) {
3530 if (!add_to_selection) {
3533 note_selected (*i, true, false);
3540 /* use the last one */
3542 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3543 unique_select (*(_events.rbegin()));
3548 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3550 bool had_selected = false;
3552 time_sort_events ();
3554 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3555 if ((*i)->selected()) {
3556 selected.insert ((*i)->note());
3557 had_selected = true;
3561 if (allow_all_if_none_selected && !had_selected) {
3562 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3563 selected.insert ((*i)->note());
3569 MidiRegionView::update_ghost_note (double x, double y)
3571 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3576 _note_group->canvas_to_item (x, y);
3578 PublicEditor& editor = trackview.editor ();
3580 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3581 framecnt_t grid_frames;
3582 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3584 /* use region_frames... because we are converting a delta within the region
3587 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3589 /* note that this sets the time of the ghost note in beats relative to
3590 the start of the source; that is how all note times are stored.
3592 _ghost_note->note()->set_time (
3593 std::max(Evoral::MusicalTime(),
3594 absolute_frames_to_source_beats (f + _region->position ())));
3595 _ghost_note->note()->set_length (length);
3596 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3597 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3599 /* the ghost note does not appear in ghost regions, so pass false in here */
3600 update_note (_ghost_note, false);
3602 show_verbose_cursor (_ghost_note->note ());
3606 MidiRegionView::create_ghost_note (double x, double y)
3608 remove_ghost_note ();
3610 boost::shared_ptr<NoteType> g (new NoteType);
3611 _ghost_note = new Note (*this, _note_group, g);
3612 _ghost_note->set_ignore_events (true);
3613 _ghost_note->set_outline_color (0x000000aa);
3614 if (x < 0) { x = 0; }
3615 update_ghost_note (x, y);
3616 _ghost_note->show ();
3621 show_verbose_cursor (_ghost_note->note ());
3625 MidiRegionView::remove_ghost_note ()
3632 MidiRegionView::snap_changed ()
3638 create_ghost_note (_last_ghost_x, _last_ghost_y);
3642 MidiRegionView::drop_down_keys ()
3644 _mouse_state = None;
3648 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3650 /* XXX: This is dead code. What was it for? */
3652 double note = midi_stream_view()->y_to_note(y);
3654 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3656 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3658 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3659 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3660 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3661 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3666 bool add_mrv_selection = false;
3668 if (_selection.empty()) {
3669 add_mrv_selection = true;
3672 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3673 if (_selection.insert (*i).second) {
3674 (*i)->set_selected (true);
3678 if (add_mrv_selection) {
3679 PublicEditor& editor (trackview.editor());
3680 editor.get_selection().add (this);
3685 MidiRegionView::color_handler ()
3687 RegionView::color_handler ();
3689 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3690 (*i)->set_selected ((*i)->selected()); // will change color
3693 /* XXX probably more to do here */
3697 MidiRegionView::enable_display (bool yn)
3699 RegionView::enable_display (yn);
3706 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3708 if (_step_edit_cursor == 0) {
3709 ArdourCanvas::Item* const group = get_canvas_group();
3711 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3712 _step_edit_cursor->set_y0 (0);
3713 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3714 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3715 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3718 move_step_edit_cursor (pos);
3719 _step_edit_cursor->show ();
3723 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3725 _step_edit_cursor_position = pos;
3727 if (_step_edit_cursor) {
3728 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3729 _step_edit_cursor->set_x0 (pixel);
3730 set_step_edit_cursor_width (_step_edit_cursor_width);
3735 MidiRegionView::hide_step_edit_cursor ()
3737 if (_step_edit_cursor) {
3738 _step_edit_cursor->hide ();
3743 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3745 _step_edit_cursor_width = beats;
3747 if (_step_edit_cursor) {
3748 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3752 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3753 * @param w Source that the data will end up in.
3756 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3758 if (!_active_notes) {
3759 /* we aren't actively being recorded to */
3763 boost::shared_ptr<MidiSource> src = w.lock ();
3764 if (!src || src != midi_region()->midi_source()) {
3765 /* recorded data was not destined for our source */
3769 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3771 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3773 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3775 framepos_t back = max_framepos;
3777 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3778 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3780 if (ev.is_channel_event()) {
3781 if (get_channel_mode() == FilterChannels) {
3782 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3788 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3789 frames from the start of the source, and so time_beats is in terms of the
3793 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3795 if (ev.type() == MIDI_CMD_NOTE_ON) {
3796 boost::shared_ptr<NoteType> note (
3797 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3799 add_note (note, true);
3801 /* fix up our note range */
3802 if (ev.note() < _current_range_min) {
3803 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3804 } else if (ev.note() > _current_range_max) {
3805 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3808 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3809 resolve_note (ev.note (), time_beats);
3815 midi_stream_view()->check_record_layers (region(), back);
3819 MidiRegionView::trim_front_starting ()
3821 /* Reparent the note group to the region view's parent, so that it doesn't change
3822 when the region view is trimmed.
3824 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3825 _temporary_note_group->move (group->position ());
3826 _note_group->reparent (_temporary_note_group);
3830 MidiRegionView::trim_front_ending ()
3832 _note_group->reparent (group);
3833 delete _temporary_note_group;
3834 _temporary_note_group = 0;
3836 if (_region->start() < 0) {
3837 /* Trim drag made start time -ve; fix this */
3838 midi_region()->fix_negative_start ();
3843 MidiRegionView::edit_patch_change (PatchChange* pc)
3845 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3847 int response = d.run();
3850 case Gtk::RESPONSE_ACCEPT:
3852 case Gtk::RESPONSE_REJECT:
3853 delete_patch_change (pc);
3859 change_patch_change (pc->patch(), d.patch ());
3863 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3866 // sysyex object doesn't have a pointer to a sysex event
3867 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3868 // c->remove (sysex->sysex());
3869 // _model->apply_command (*trackview.session(), c);
3871 //_sys_exes.clear ();
3872 // display_sysexes();
3876 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3878 using namespace MIDI::Name;
3882 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3884 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3886 MIDI::Name::PatchPrimaryKey patch_key;
3887 get_patch_key_at(n->time(), n->channel(), patch_key);
3888 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3890 patch_key.bank_number,
3891 patch_key.program_number,
3897 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3899 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3900 (int) n->channel() + 1,
3901 (int) n->velocity());
3903 show_verbose_cursor(buf, 10, 20);
3907 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3909 trackview.editor().verbose_cursor()->set (text);
3910 trackview.editor().verbose_cursor()->show ();
3911 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3914 /** @param p A session framepos.
3915 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3916 * @return p snapped to the grid subdivision underneath it.
3919 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3921 PublicEditor& editor = trackview.editor ();
3923 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3925 grid_frames = region_beats_to_region_frames (grid_beats);
3927 /* Hack so that we always snap to the note that we are over, instead of snapping
3928 to the next one if we're more than halfway through the one we're over.
3930 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3931 p -= grid_frames / 2;
3934 return snap_frame_to_frame (p);
3937 /** Called when the selection has been cleared in any MidiRegionView.
3938 * @param rv MidiRegionView that the selection was cleared in.
3941 MidiRegionView::selection_cleared (MidiRegionView* rv)
3947 /* Clear our selection in sympathy; but don't signal the fact */
3948 clear_selection (false);
3952 MidiRegionView::note_button_release ()
3954 delete _note_player;
3959 MidiRegionView::get_channel_mode () const
3961 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3962 return rtav->midi_track()->get_playback_channel_mode();
3966 MidiRegionView::get_selected_channels () const
3968 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3969 return rtav->midi_track()->get_playback_channel_mask();
3974 MidiRegionView::get_grid_beats(framepos_t pos) const
3976 PublicEditor& editor = trackview.editor();
3977 bool success = false;
3978 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3980 beats = Evoral::MusicalTime(1);