2 Copyright (C) 2001-2011 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "gtkmm2ext/gtk_ui.h"
28 #include <sigc++/signal.h>
30 #include "midi++/midnam_patch.h"
32 #include "pbd/memento_command.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "ardour/midi_model.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIEvent.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "canvas/debug.h"
48 #include "canvas/text.h"
50 #include "automation_region_view.h"
51 #include "automation_time_axis.h"
52 #include "control_point.h"
55 #include "editor_drag.h"
56 #include "ghostregion.h"
57 #include "gui_thread.h"
58 #include "item_counts.h"
60 #include "midi_channel_dialog.h"
61 #include "midi_cut_buffer.h"
62 #include "midi_list_editor.h"
63 #include "midi_region_view.h"
64 #include "midi_streamview.h"
65 #include "midi_time_axis.h"
66 #include "midi_util.h"
67 #include "midi_velocity_dialog.h"
68 #include "mouse_cursors.h"
69 #include "note_player.h"
70 #include "paste_context.h"
71 #include "public_editor.h"
72 #include "route_time_axis.h"
73 #include "rgb_macros.h"
74 #include "selection.h"
75 #include "streamview.h"
76 #include "patch_change_dialog.h"
77 #include "verbose_cursor.h"
78 #include "ardour_ui.h"
81 #include "patch_change.h"
86 using namespace ARDOUR;
88 using namespace Editing;
90 using Gtkmm2ext::Keyboard;
92 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
94 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
96 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
97 RouteTimeAxisView& tv,
98 boost::shared_ptr<MidiRegion> r,
100 uint32_t basic_color)
101 : RegionView (parent, tv, r, spu, basic_color)
102 , _current_range_min(0)
103 , _current_range_max(0)
104 , _region_relative_time_converter(r->session().tempo_map(), r->position())
105 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
107 , _note_group (new ArdourCanvas::Container (group))
108 , _note_diff_command (0)
110 , _step_edit_cursor (0)
111 , _step_edit_cursor_width (1.0)
112 , _step_edit_cursor_position (0.0)
113 , _channel_selection_scoped_note (0)
114 , _temporary_note_group (0)
117 , _sort_needed (true)
118 , _optimization_iterator (_events.end())
120 , _no_sound_notes (false)
123 , _grabbed_keyboard (false)
125 , pre_enter_cursor (0)
126 , pre_press_cursor (0)
127 , pre_note_enter_cursor (0)
130 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
131 _note_group->raise_to_top();
132 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
134 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
135 connect_to_diskstream ();
137 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
140 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
141 RouteTimeAxisView& tv,
142 boost::shared_ptr<MidiRegion> r,
144 uint32_t basic_color,
146 TimeAxisViewItem::Visibility visibility)
147 : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
148 , _current_range_min(0)
149 , _current_range_max(0)
150 , _region_relative_time_converter(r->session().tempo_map(), r->position())
151 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
153 , _note_group (new ArdourCanvas::Container (parent))
154 , _note_diff_command (0)
156 , _step_edit_cursor (0)
157 , _step_edit_cursor_width (1.0)
158 , _step_edit_cursor_position (0.0)
159 , _channel_selection_scoped_note (0)
160 , _temporary_note_group (0)
163 , _sort_needed (true)
164 , _optimization_iterator (_events.end())
166 , _no_sound_notes (false)
169 , _grabbed_keyboard (false)
171 , pre_enter_cursor (0)
172 , pre_press_cursor (0)
173 , pre_note_enter_cursor (0)
176 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
177 _note_group->raise_to_top();
179 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
181 connect_to_diskstream ();
183 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
187 MidiRegionView::parameter_changed (std::string const & p)
189 if (p == "display-first-midi-bank-as-zero") {
190 if (_enable_display) {
196 MidiRegionView::MidiRegionView (const MidiRegionView& other)
197 : sigc::trackable(other)
199 , _current_range_min(0)
200 , _current_range_max(0)
201 , _region_relative_time_converter(other.region_relative_time_converter())
202 , _source_relative_time_converter(other.source_relative_time_converter())
204 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
205 , _note_diff_command (0)
207 , _step_edit_cursor (0)
208 , _step_edit_cursor_width (1.0)
209 , _step_edit_cursor_position (0.0)
210 , _channel_selection_scoped_note (0)
211 , _temporary_note_group (0)
214 , _sort_needed (true)
215 , _optimization_iterator (_events.end())
217 , _no_sound_notes (false)
220 , _grabbed_keyboard (false)
222 , pre_enter_cursor (0)
223 , pre_press_cursor (0)
224 , pre_note_enter_cursor (0)
230 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
231 : RegionView (other, boost::shared_ptr<Region> (region))
232 , _current_range_min(0)
233 , _current_range_max(0)
234 , _region_relative_time_converter(other.region_relative_time_converter())
235 , _source_relative_time_converter(other.source_relative_time_converter())
237 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
238 , _note_diff_command (0)
240 , _step_edit_cursor (0)
241 , _step_edit_cursor_width (1.0)
242 , _step_edit_cursor_position (0.0)
243 , _channel_selection_scoped_note (0)
244 , _temporary_note_group (0)
247 , _sort_needed (true)
248 , _optimization_iterator (_events.end())
250 , _no_sound_notes (false)
253 , _grabbed_keyboard (false)
255 , pre_enter_cursor (0)
256 , pre_press_cursor (0)
257 , pre_note_enter_cursor (0)
264 MidiRegionView::init (bool wfd)
266 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
268 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
269 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
273 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
274 midi_region()->midi_source(0)->load_model(lm);
277 _model = midi_region()->midi_source(0)->model();
278 _enable_display = false;
279 _fill_color_name = "midi frame base";
281 RegionView::init (false);
283 set_height (trackview.current_height());
286 region_sync_changed ();
287 region_resized (ARDOUR::bounds_change);
292 _enable_display = true;
295 display_model (_model);
299 reset_width_dependent_items (_pixel_width);
301 group->raise_to_top();
303 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
304 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
307 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
308 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
310 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
311 boost::bind (&MidiRegionView::snap_changed, this),
314 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
315 connect_to_diskstream ();
317 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
321 MidiRegionView::instrument_info () const
323 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
324 return route_ui->route()->instrument_info();
327 const boost::shared_ptr<ARDOUR::MidiRegion>
328 MidiRegionView::midi_region() const
330 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
334 MidiRegionView::connect_to_diskstream ()
336 midi_view()->midi_track()->DataRecorded.connect(
337 *this, invalidator(*this),
338 boost::bind (&MidiRegionView::data_recorded, this, _1),
343 MidiRegionView::canvas_group_event(GdkEvent* ev)
345 if (in_destructor || _recregion) {
349 if (!trackview.editor().internal_editing()) {
350 // not in internal edit mode, so just act like a normal region
351 return RegionView::canvas_group_event (ev);
354 const MouseMode m = trackview.editor().current_mouse_mode();
358 case GDK_ENTER_NOTIFY:
359 _last_event_x = ev->crossing.x;
360 _last_event_y = ev->crossing.y;
361 enter_notify(&ev->crossing);
362 // set entered_regionview (among other things)
363 return RegionView::canvas_group_event (ev);
365 case GDK_LEAVE_NOTIFY:
366 _last_event_x = ev->crossing.x;
367 _last_event_y = ev->crossing.y;
368 leave_notify(&ev->crossing);
369 // reset entered_regionview (among other things)
370 return RegionView::canvas_group_event (ev);
372 case GDK_2BUTTON_PRESS:
373 // cannot use double-click to exit internal mode if single-click is being used
374 if ((m != MouseDraw) &&
376 !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier()))) {
377 return trackview.editor().toggle_internal_editing_from_double_click (ev);
382 if (scroll (&ev->scroll)) {
388 return key_press (&ev->key);
390 case GDK_KEY_RELEASE:
391 return key_release (&ev->key);
393 case GDK_BUTTON_PRESS:
394 return button_press (&ev->button);
396 case GDK_BUTTON_RELEASE:
397 r = button_release (&ev->button);
402 case GDK_MOTION_NOTIFY:
403 _last_event_x = ev->motion.x;
404 _last_event_y = ev->motion.y;
405 return motion (&ev->motion);
411 return RegionView::canvas_group_event (ev);
415 MidiRegionView::enter_notify (GdkEventCrossing* ev)
417 trackview.editor().MouseModeChanged.connect (
418 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
428 MidiRegionView::leave_notify (GdkEventCrossing*)
430 _mouse_mode_connection.disconnect ();
439 MidiRegionView::mouse_mode_changed ()
441 if (trackview.editor().internal_editing()) {
442 // Switched in to internal editing mode while entered
445 // Switched out of internal editing mode while entered
451 MidiRegionView::enter_internal()
453 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
454 // Show ghost note under pencil
455 create_ghost_note(_last_event_x, _last_event_y);
458 if (!_selection.empty()) {
459 // Grab keyboard for moving selected notes with arrow keys
460 Keyboard::magic_widget_grab_focus();
461 _grabbed_keyboard = true;
466 MidiRegionView::leave_internal()
468 trackview.editor().verbose_cursor()->hide ();
469 remove_ghost_note ();
471 if (_grabbed_keyboard) {
472 Keyboard::magic_widget_drop_focus();
473 _grabbed_keyboard = false;
478 MidiRegionView::button_press (GdkEventButton* ev)
480 if (ev->button != 1) {
484 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
485 MouseMode m = editor->current_mouse_mode();
487 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
488 pre_press_cursor = editor->get_canvas_cursor ();
489 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
492 if (_mouse_state != SelectTouchDragging) {
494 _pressed_button = ev->button;
495 _mouse_state = Pressed;
500 _pressed_button = ev->button;
506 MidiRegionView::button_release (GdkEventButton* ev)
508 double event_x, event_y;
510 if (ev->button != 1) {
517 group->canvas_to_item (event_x, event_y);
520 PublicEditor& editor = trackview.editor ();
522 if (pre_press_cursor) {
523 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
524 pre_press_cursor = 0;
527 switch (_mouse_state) {
528 case Pressed: // Clicked
530 switch (editor.current_mouse_mode()) {
532 /* no motion occured - simple click */
541 if (Keyboard::is_insert_note_event(ev)) {
543 double event_x, event_y;
547 group->canvas_to_item (event_x, event_y);
549 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
551 /* Shorten the length by 1 tick so that we can add a new note at the next
552 grid snap without it overlapping this one.
554 beats -= Evoral::MusicalTime::tick();
556 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
563 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
565 /* Shorten the length by 1 tick so that we can add a new note at the next
566 grid snap without it overlapping this one.
568 beats -= Evoral::MusicalTime::tick();
570 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
581 case SelectRectDragging:
583 editor.drags()->end_grab ((GdkEvent *) ev);
585 create_ghost_note (ev->x, ev->y);
597 MidiRegionView::motion (GdkEventMotion* ev)
599 PublicEditor& editor = trackview.editor ();
601 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
602 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
603 _mouse_state != AddDragging) {
605 create_ghost_note (ev->x, ev->y);
607 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
608 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
610 update_ghost_note (ev->x, ev->y);
612 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
614 remove_ghost_note ();
615 editor.verbose_cursor()->hide ();
617 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
619 update_ghost_note (ev->x, ev->y);
622 /* any motion immediately hides velocity text that may have been visible */
624 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
625 (*i)->hide_velocity ();
628 switch (_mouse_state) {
631 if (_pressed_button == 1) {
633 MouseMode m = editor.current_mouse_mode();
635 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
636 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
637 _mouse_state = AddDragging;
638 remove_ghost_note ();
639 editor.verbose_cursor()->hide ();
641 } else if (m == MouseObject) {
642 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
643 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
646 _mouse_state = SelectRectDragging;
648 } else if (m == MouseRange) {
649 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
650 _mouse_state = SelectVerticalDragging;
657 case SelectRectDragging:
658 case SelectVerticalDragging:
660 editor.drags()->motion_handler ((GdkEvent *) ev, false);
663 case SelectTouchDragging:
671 /* we may be dragging some non-note object (eg. patch-change, sysex)
674 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
679 MidiRegionView::scroll (GdkEventScroll* ev)
681 if (_selection.empty()) {
685 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
686 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
687 it still works for zoom.
692 trackview.editor().verbose_cursor()->hide ();
694 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
695 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
697 if (ev->direction == GDK_SCROLL_UP) {
698 change_velocities (true, fine, false, together);
699 } else if (ev->direction == GDK_SCROLL_DOWN) {
700 change_velocities (false, fine, false, together);
702 /* left, right: we don't use them */
710 MidiRegionView::key_press (GdkEventKey* ev)
712 /* since GTK bindings are generally activated on press, and since
713 detectable auto-repeat is the name of the game and only sends
714 repeated presses, carry out key actions at key press, not release.
717 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
719 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
720 _mouse_state = SelectTouchDragging;
723 } else if (ev->keyval == GDK_Escape && unmodified) {
727 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
729 bool start = (ev->keyval == GDK_comma);
730 bool end = (ev->keyval == GDK_period);
731 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
732 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
734 change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
738 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
740 if (_selection.empty()) {
747 } else if (ev->keyval == GDK_Tab) {
749 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
750 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
752 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
756 } else if (ev->keyval == GDK_ISO_Left_Tab) {
758 /* Shift-TAB generates ISO Left Tab, for some reason */
760 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
761 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
763 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
769 } else if (ev->keyval == GDK_Up) {
771 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
772 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
773 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
775 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
776 change_velocities (true, fine, allow_smush, together);
778 transpose (true, fine, allow_smush);
782 } else if (ev->keyval == GDK_Down) {
784 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
785 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
786 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
788 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
789 change_velocities (false, fine, allow_smush, together);
791 transpose (false, fine, allow_smush);
795 } else if (ev->keyval == GDK_Left) {
797 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
798 nudge_notes (false, fine);
801 } else if (ev->keyval == GDK_Right) {
803 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
804 nudge_notes (true, fine);
807 } else if (ev->keyval == GDK_c && unmodified) {
811 } else if (ev->keyval == GDK_v && unmodified) {
820 MidiRegionView::key_release (GdkEventKey* ev)
822 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
830 MidiRegionView::channel_edit ()
832 if (_selection.empty()) {
836 /* pick a note somewhat at random (since Selection is a set<>) to
837 * provide the "current" channel for the dialog.
840 uint8_t current_channel = (*_selection.begin())->note()->channel ();
841 MidiChannelDialog channel_dialog (current_channel);
842 int ret = channel_dialog.run ();
845 case Gtk::RESPONSE_OK:
851 uint8_t new_channel = channel_dialog.active_channel ();
853 start_note_diff_command (_("channel edit"));
855 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
856 Selection::iterator next = i;
858 change_note_channel (*i, new_channel);
866 MidiRegionView::velocity_edit ()
868 if (_selection.empty()) {
872 /* pick a note somewhat at random (since Selection is a set<>) to
873 * provide the "current" velocity for the dialog.
876 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
877 MidiVelocityDialog velocity_dialog (current_velocity);
878 int ret = velocity_dialog.run ();
881 case Gtk::RESPONSE_OK:
887 uint8_t new_velocity = velocity_dialog.velocity ();
889 start_note_diff_command (_("velocity edit"));
891 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
892 Selection::iterator next = i;
894 change_note_velocity (*i, new_velocity, false);
902 MidiRegionView::show_list_editor ()
905 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
907 _list_editor->present ();
910 /** Add a note to the model, and the view, at a canvas (click) coordinate.
911 * \param t time in frames relative to the position of the region
912 * \param y vertical position in pixels
913 * \param length duration of the note in beats
914 * \param snap_t true to snap t to the grid, otherwise false.
917 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
919 if (length < 2 * DBL_EPSILON) {
923 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
924 MidiStreamView* const view = mtv->midi_view();
926 const double note = view->y_to_note(y);
928 // Start of note in frames relative to region start
930 framecnt_t grid_frames;
931 t = snap_frame_to_grid_underneath (t, grid_frames);
934 const boost::shared_ptr<NoteType> new_note (
935 new NoteType (mtv->get_channel_for_add (),
936 region_frames_to_region_beats(t + _region->start()),
938 (uint8_t)note, 0x40));
940 if (_model->contains (new_note)) {
944 view->update_note_range(new_note->note());
946 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
948 _model->apply_command(*trackview.session(), cmd);
950 play_midi_note (new_note);
954 MidiRegionView::clear_events (bool with_selection_signal)
956 clear_selection (with_selection_signal);
959 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
960 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
965 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
970 _patch_changes.clear();
972 _optimization_iterator = _events.end();
976 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
980 content_connection.disconnect ();
981 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
985 if (_enable_display) {
991 MidiRegionView::start_note_diff_command (string name)
993 if (!_note_diff_command) {
994 _note_diff_command = _model->new_note_diff_command (name);
999 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1001 if (_note_diff_command) {
1002 _note_diff_command->add (note);
1005 _marked_for_selection.insert(note);
1007 if (show_velocity) {
1008 _marked_for_velocity.insert(note);
1013 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1015 if (_note_diff_command && ev->note()) {
1016 _note_diff_command->remove(ev->note());
1021 MidiRegionView::note_diff_add_change (NoteBase* ev,
1022 MidiModel::NoteDiffCommand::Property property,
1025 if (_note_diff_command) {
1026 _note_diff_command->change (ev->note(), property, val);
1031 MidiRegionView::note_diff_add_change (NoteBase* ev,
1032 MidiModel::NoteDiffCommand::Property property,
1033 Evoral::MusicalTime val)
1035 if (_note_diff_command) {
1036 _note_diff_command->change (ev->note(), property, val);
1041 MidiRegionView::apply_diff (bool as_subcommand)
1045 if (!_note_diff_command) {
1049 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1050 // Mark all selected notes for selection when model reloads
1051 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1052 _marked_for_selection.insert((*i)->note());
1056 if (as_subcommand) {
1057 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1059 _model->apply_command (*trackview.session(), _note_diff_command);
1062 _note_diff_command = 0;
1063 midi_view()->midi_track()->playlist_modified();
1065 if (add_or_remove) {
1066 _marked_for_selection.clear();
1069 _marked_for_velocity.clear();
1073 MidiRegionView::abort_command()
1075 delete _note_diff_command;
1076 _note_diff_command = 0;
1081 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1083 if (_optimization_iterator != _events.end()) {
1084 ++_optimization_iterator;
1087 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1088 return *_optimization_iterator;
1091 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1092 if ((*_optimization_iterator)->note() == note) {
1093 return *_optimization_iterator;
1101 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1103 MidiModel::Notes notes;
1104 _model->get_notes (notes, op, val, chan_mask);
1106 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1107 NoteBase* cne = find_canvas_note (*n);
1115 MidiRegionView::redisplay_model()
1117 // Don't redisplay the model if we're currently recording and displaying that
1118 if (_active_notes) {
1126 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1127 (*i)->invalidate ();
1130 MidiModel::ReadLock lock(_model->read_lock());
1132 MidiModel::Notes& notes (_model->notes());
1133 _optimization_iterator = _events.begin();
1135 bool empty_when_starting = _events.empty();
1137 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1139 boost::shared_ptr<NoteType> note (*n);
1143 if (note_in_region_range (note, visible)) {
1145 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1152 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1154 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1166 add_note (note, visible);
1171 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1179 /* remove note items that are no longer valid */
1181 if (!empty_when_starting) {
1182 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1183 if (!(*i)->valid ()) {
1185 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1186 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1188 gr->remove_note (*i);
1193 i = _events.erase (i);
1201 _patch_changes.clear();
1205 display_patch_changes ();
1207 _marked_for_selection.clear ();
1208 _marked_for_velocity.clear ();
1210 /* we may have caused _events to contain things out of order (e.g. if a note
1211 moved earlier or later). we don't generally need them in time order, but
1212 make a note that a sort is required for those cases that require it.
1215 _sort_needed = true;
1219 MidiRegionView::display_patch_changes ()
1221 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1222 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1224 for (uint8_t i = 0; i < 16; ++i) {
1225 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1229 /** @param active_channel true to display patch changes fully, false to display
1230 * them `greyed-out' (as on an inactive channel)
1233 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1235 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1237 if ((*i)->channel() != channel) {
1241 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1242 add_canvas_patch_change (*i, patch_name, active_channel);
1247 MidiRegionView::display_sysexes()
1249 bool have_periodic_system_messages = false;
1250 bool display_periodic_messages = true;
1252 if (!Config->get_never_display_periodic_midi()) {
1254 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1255 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1256 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1259 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1260 have_periodic_system_messages = true;
1266 if (have_periodic_system_messages) {
1267 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1269 /* get an approximate value for the number of samples per video frame */
1271 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1273 /* if we are zoomed out beyond than the cutoff (i.e. more
1274 * frames per pixel than frames per 4 video frames), don't
1275 * show periodic sysex messages.
1278 if (zoom > (video_frame*4)) {
1279 display_periodic_messages = false;
1283 display_periodic_messages = false;
1286 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1288 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1289 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1291 Evoral::MusicalTime time = (*i)->time();
1294 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1295 if (!display_periodic_messages) {
1303 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1304 str << int((*i)->buffer()[b]);
1305 if (b != (*i)->size() -1) {
1309 string text = str.str();
1311 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1313 double height = midi_stream_view()->contents_height();
1315 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1316 // SysEx canvas object!!!
1318 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1319 new SysEx (*this, _note_group, text, height, x, 1.0));
1321 // Show unless message is beyond the region bounds
1322 if (time - _region->start() >= _region->length() || time < _region->start()) {
1328 _sys_exes.push_back(sysex);
1332 MidiRegionView::~MidiRegionView ()
1334 in_destructor = true;
1336 trackview.editor().verbose_cursor()->hide ();
1338 note_delete_connection.disconnect ();
1340 delete _list_editor;
1342 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1344 if (_active_notes) {
1348 _selection_cleared_connection.disconnect ();
1351 clear_events (false);
1354 delete _note_diff_command;
1355 delete _step_edit_cursor;
1356 delete _temporary_note_group;
1360 MidiRegionView::region_resized (const PropertyChange& what_changed)
1362 RegionView::region_resized(what_changed);
1364 if (what_changed.contains (ARDOUR::Properties::position)) {
1365 _region_relative_time_converter.set_origin_b(_region->position());
1366 set_duration(_region->length(), 0);
1367 if (_enable_display) {
1372 if (what_changed.contains (ARDOUR::Properties::start) ||
1373 what_changed.contains (ARDOUR::Properties::position)) {
1374 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1379 MidiRegionView::reset_width_dependent_items (double pixel_width)
1381 RegionView::reset_width_dependent_items(pixel_width);
1383 if (_enable_display) {
1387 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1388 if ((*x)->canvas_item()->width() >= _pixel_width) {
1395 move_step_edit_cursor (_step_edit_cursor_position);
1396 set_step_edit_cursor_width (_step_edit_cursor_width);
1400 MidiRegionView::set_height (double height)
1402 double old_height = _height;
1403 RegionView::set_height(height);
1405 apply_note_range (midi_stream_view()->lowest_note(),
1406 midi_stream_view()->highest_note(),
1407 height != old_height);
1410 name_text->raise_to_top();
1413 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1414 (*x)->set_height (midi_stream_view()->contents_height());
1417 if (_step_edit_cursor) {
1418 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1423 /** Apply the current note range from the stream view
1424 * by repositioning/hiding notes as necessary
1427 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1429 if (!_enable_display) {
1433 if (!force && _current_range_min == min && _current_range_max == max) {
1437 _current_range_min = min;
1438 _current_range_max = max;
1440 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1441 NoteBase* event = *i;
1442 boost::shared_ptr<NoteType> note (event->note());
1444 if (note->note() < _current_range_min ||
1445 note->note() > _current_range_max) {
1451 if (Note* cnote = dynamic_cast<Note*>(event)) {
1453 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1454 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1459 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1466 MidiRegionView::add_ghost (TimeAxisView& tv)
1470 double unit_position = _region->position () / samples_per_pixel;
1471 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1472 MidiGhostRegion* ghost;
1474 if (mtv && mtv->midi_view()) {
1475 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1476 to allow having midi notes on top of note lines and waveforms.
1478 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1480 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1483 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1484 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1485 ghost->add_note(note);
1489 ghost->set_height ();
1490 ghost->set_duration (_region->length() / samples_per_pixel);
1491 ghosts.push_back (ghost);
1493 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1499 /** Begin tracking note state for successive calls to add_event
1502 MidiRegionView::begin_write()
1504 if (_active_notes) {
1505 delete[] _active_notes;
1507 _active_notes = new Note*[128];
1508 for (unsigned i = 0; i < 128; ++i) {
1509 _active_notes[i] = 0;
1514 /** Destroy note state for add_event
1517 MidiRegionView::end_write()
1519 delete[] _active_notes;
1521 _marked_for_selection.clear();
1522 _marked_for_velocity.clear();
1526 /** Resolve an active MIDI note (while recording).
1529 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1531 if (midi_view()->note_mode() != Sustained) {
1535 if (_active_notes && _active_notes[note]) {
1537 /* XXX is end_time really region-centric? I think so, because
1538 this is a new region that we're recording, so source zero is
1539 the same as region zero
1541 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1543 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1544 _active_notes[note]->set_outline_all ();
1545 _active_notes[note] = 0;
1551 /** Extend active notes to rightmost edge of region (if length is changed)
1554 MidiRegionView::extend_active_notes()
1556 if (!_active_notes) {
1560 for (unsigned i=0; i < 128; ++i) {
1561 if (_active_notes[i]) {
1562 _active_notes[i]->set_x1(
1563 trackview.editor().sample_to_pixel(_region->position() + _region->length()));
1570 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1572 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1576 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1578 if (!route_ui || !route_ui->midi_track()) {
1582 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1586 /* NotePlayer deletes itself */
1590 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1592 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1596 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1598 if (!route_ui || !route_ui->midi_track()) {
1602 delete _note_player;
1603 _note_player = new NotePlayer (route_ui->midi_track ());
1604 _note_player->add (note);
1605 _note_player->on ();
1609 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1611 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1615 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1617 if (!route_ui || !route_ui->midi_track()) {
1621 delete _note_player;
1622 _note_player = new NotePlayer (route_ui->midi_track());
1624 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1625 _note_player->add (*n);
1628 _note_player->on ();
1633 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1635 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1636 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1638 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1639 (note->note() <= midi_stream_view()->highest_note());
1644 /** Update a canvas note's size from its model note.
1645 * @param ev Canvas note to update.
1646 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1649 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1651 boost::shared_ptr<NoteType> note = ev->note();
1652 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1653 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1658 /* trim note display to not overlap the end of its region */
1660 if (note->length() > 0) {
1661 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1662 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1664 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1667 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1669 if (!note->length()) {
1670 if (_active_notes && note->note() < 128) {
1671 // If this note is already active there's a stuck note,
1672 // finish the old note rectangle
1673 if (_active_notes[note->note()]) {
1674 Note* const old_rect = _active_notes[note->note()];
1675 boost::shared_ptr<NoteType> old_note = old_rect->note();
1676 old_rect->set_x1 (x);
1677 old_rect->set_outline_all ();
1679 _active_notes[note->note()] = ev;
1681 /* outline all but right edge */
1682 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1683 ArdourCanvas::Rectangle::TOP|
1684 ArdourCanvas::Rectangle::LEFT|
1685 ArdourCanvas::Rectangle::BOTTOM));
1687 /* outline all edges */
1688 ev->set_outline_all ();
1691 if (update_ghost_regions) {
1692 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1693 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1695 gr->update_note (ev);
1702 MidiRegionView::update_hit (Hit* ev)
1704 boost::shared_ptr<NoteType> note = ev->note();
1706 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1707 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1708 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1709 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1711 ev->set_position (ArdourCanvas::Duple (x, y));
1712 ev->set_height (diamond_size);
1715 /** Add a MIDI note to the view (with length).
1717 * If in sustained mode, notes with length 0 will be considered active
1718 * notes, and resolve_note should be called when the corresponding note off
1719 * event arrives, to properly display the note.
1722 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1724 NoteBase* event = 0;
1726 if (midi_view()->note_mode() == Sustained) {
1728 Note* ev_rect = new Note (*this, _note_group, note);
1730 update_note (ev_rect);
1734 MidiGhostRegion* gr;
1736 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1737 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1738 gr->add_note(ev_rect);
1742 } else if (midi_view()->note_mode() == Percussive) {
1744 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1746 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1748 update_hit (ev_diamond);
1757 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1758 note_selected(event, true);
1761 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1762 event->show_velocity();
1765 event->on_channel_selection_change (get_selected_channels());
1766 _events.push_back(event);
1775 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1776 MidiStreamView* const view = mtv->midi_view();
1778 view->update_note_range (note->note());
1782 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1783 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1785 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1787 /* potentially extend region to hold new note */
1789 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1790 framepos_t region_end = _region->last_frame();
1792 if (end_frame > region_end) {
1793 _region->set_length (end_frame - _region->position());
1796 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1797 MidiStreamView* const view = mtv->midi_view();
1799 view->update_note_range(new_note->note());
1801 _marked_for_selection.clear ();
1804 start_note_diff_command (_("step add"));
1805 note_diff_add_note (new_note, true, false);
1808 // last_step_edit_note = new_note;
1812 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1814 change_note_lengths (false, false, beats, false, true);
1817 /** Add a new patch change flag to the canvas.
1818 * @param patch the patch change to add
1819 * @param the text to display in the flag
1820 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1823 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1825 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1826 const double x = trackview.editor().sample_to_pixel (region_frames);
1828 double const height = midi_stream_view()->contents_height();
1830 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1831 // so we need to do something more sophisticated to keep its color
1832 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1835 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1836 new PatchChange(*this, group,
1843 if (patch_change->item().width() < _pixel_width) {
1844 // Show unless patch change is beyond the region bounds
1845 if (region_frames < 0 || region_frames >= _region->length()) {
1846 patch_change->hide();
1848 patch_change->show();
1851 patch_change->hide ();
1854 _patch_changes.push_back (patch_change);
1857 MIDI::Name::PatchPrimaryKey
1858 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1860 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1863 /// Return true iff @p pc applies to the given time on the given channel.
1865 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1867 return pc->time() <= time && pc->channel() == channel;
1871 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1873 // The earliest event not before time
1874 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1876 // Go backwards until we find the latest PC for this channel, or the start
1877 while (i != _model->patch_changes().begin() &&
1878 (i == _model->patch_changes().end() ||
1879 !patch_applies(*i, time, channel))) {
1883 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1884 key.bank_number = (*i)->bank();
1885 key.program_number = (*i)->program ();
1887 key.bank_number = key.program_number = 0;
1890 if (!key.is_sane()) {
1891 error << string_compose(_("insane MIDI patch key %1:%2"),
1892 key.bank_number, key.program_number) << endmsg;
1897 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1899 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1901 if (pc.patch()->program() != new_patch.program_number) {
1902 c->change_program (pc.patch (), new_patch.program_number);
1905 int const new_bank = new_patch.bank_number;
1906 if (pc.patch()->bank() != new_bank) {
1907 c->change_bank (pc.patch (), new_bank);
1910 _model->apply_command (*trackview.session(), c);
1912 _patch_changes.clear ();
1913 display_patch_changes ();
1917 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1919 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1921 if (old_change->time() != new_change.time()) {
1922 c->change_time (old_change, new_change.time());
1925 if (old_change->channel() != new_change.channel()) {
1926 c->change_channel (old_change, new_change.channel());
1929 if (old_change->program() != new_change.program()) {
1930 c->change_program (old_change, new_change.program());
1933 if (old_change->bank() != new_change.bank()) {
1934 c->change_bank (old_change, new_change.bank());
1937 _model->apply_command (*trackview.session(), c);
1939 _patch_changes.clear ();
1940 display_patch_changes ();
1943 /** Add a patch change to the region.
1944 * @param t Time in frames relative to region position
1945 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1946 * MidiTimeAxisView::get_channel_for_add())
1949 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1951 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1953 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1954 c->add (MidiModel::PatchChangePtr (
1955 new Evoral::PatchChange<Evoral::MusicalTime> (
1956 absolute_frames_to_source_beats (_region->position() + t),
1957 mtv->get_channel_for_add(), patch.program(), patch.bank()
1962 _model->apply_command (*trackview.session(), c);
1964 _patch_changes.clear ();
1965 display_patch_changes ();
1969 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1971 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1972 c->change_time (pc.patch (), t);
1973 _model->apply_command (*trackview.session(), c);
1975 _patch_changes.clear ();
1976 display_patch_changes ();
1980 MidiRegionView::delete_patch_change (PatchChange* pc)
1982 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1983 c->remove (pc->patch ());
1984 _model->apply_command (*trackview.session(), c);
1986 _patch_changes.clear ();
1987 display_patch_changes ();
1991 MidiRegionView::previous_patch (PatchChange& patch)
1993 if (patch.patch()->program() < 127) {
1994 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1995 key.program_number++;
1996 change_patch_change (patch, key);
2001 MidiRegionView::next_patch (PatchChange& patch)
2003 if (patch.patch()->program() > 0) {
2004 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2005 key.program_number--;
2006 change_patch_change (patch, key);
2011 MidiRegionView::next_bank (PatchChange& patch)
2013 if (patch.patch()->program() < 127) {
2014 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2015 if (key.bank_number > 0) {
2017 change_patch_change (patch, key);
2023 MidiRegionView::previous_bank (PatchChange& patch)
2025 if (patch.patch()->program() > 0) {
2026 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2027 if (key.bank_number < 127) {
2029 change_patch_change (patch, key);
2035 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2037 if (_selection.empty()) {
2041 _selection.erase (cne);
2045 MidiRegionView::delete_selection()
2047 if (_selection.empty()) {
2051 start_note_diff_command (_("delete selection"));
2053 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2054 if ((*i)->selected()) {
2055 _note_diff_command->remove((*i)->note());
2065 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2067 start_note_diff_command (_("delete note"));
2068 _note_diff_command->remove (n);
2071 trackview.editor().verbose_cursor()->hide ();
2075 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2077 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2079 Selection::iterator tmp = i;
2082 (*i)->set_selected (false);
2083 (*i)->hide_velocity ();
2084 _selection.erase (i);
2092 if (!ev && _entered) {
2093 // Clearing selection entirely, ungrab keyboard
2094 Keyboard::magic_widget_drop_focus();
2095 _grabbed_keyboard = false;
2098 /* this does not change the status of this regionview w.r.t the editor
2103 SelectionCleared (this); /* EMIT SIGNAL */
2108 MidiRegionView::unique_select(NoteBase* ev)
2110 const bool selection_was_empty = _selection.empty();
2112 clear_selection_except (ev);
2114 /* don't bother with checking to see if we should remove this
2115 regionview from the editor selection, since we're about to add
2116 another note, and thus put/keep this regionview in the editor
2120 if (!ev->selected()) {
2121 add_to_selection (ev);
2122 if (selection_was_empty && _entered) {
2123 // Grab keyboard for moving notes with arrow keys
2124 Keyboard::magic_widget_grab_focus();
2125 _grabbed_keyboard = true;
2131 MidiRegionView::select_all_notes ()
2135 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2136 add_to_selection (*i);
2141 MidiRegionView::select_range (framepos_t start, framepos_t end)
2145 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2146 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2147 if (t >= start && t <= end) {
2148 add_to_selection (*i);
2154 MidiRegionView::invert_selection ()
2156 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2157 if ((*i)->selected()) {
2158 remove_from_selection(*i);
2160 add_to_selection (*i);
2166 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2168 bool have_selection = !_selection.empty();
2169 uint8_t low_note = 127;
2170 uint8_t high_note = 0;
2171 MidiModel::Notes& notes (_model->notes());
2172 _optimization_iterator = _events.begin();
2174 if (extend && !have_selection) {
2178 /* scan existing selection to get note range */
2180 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2181 if ((*i)->note()->note() < low_note) {
2182 low_note = (*i)->note()->note();
2184 if ((*i)->note()->note() > high_note) {
2185 high_note = (*i)->note()->note();
2192 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2193 /* only note previously selected is the one we are
2194 * reselecting. treat this as cancelling the selection.
2201 low_note = min (low_note, notenum);
2202 high_note = max (high_note, notenum);
2205 _no_sound_notes = true;
2207 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2209 boost::shared_ptr<NoteType> note (*n);
2211 bool select = false;
2213 if (((1 << note->channel()) & channel_mask) != 0) {
2215 if ((note->note() >= low_note && note->note() <= high_note)) {
2218 } else if (note->note() == notenum) {
2224 if ((cne = find_canvas_note (note)) != 0) {
2225 // extend is false because we've taken care of it,
2226 // since it extends by time range, not pitch.
2227 note_selected (cne, add, false);
2231 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2235 _no_sound_notes = false;
2239 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2241 MidiModel::Notes& notes (_model->notes());
2242 _optimization_iterator = _events.begin();
2244 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2246 boost::shared_ptr<NoteType> note (*n);
2249 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2250 if ((cne = find_canvas_note (note)) != 0) {
2251 if (cne->selected()) {
2252 note_deselected (cne);
2254 note_selected (cne, true, false);
2262 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2265 clear_selection_except (ev);
2266 if (!_selection.empty()) {
2267 PublicEditor& editor (trackview.editor());
2268 editor.get_selection().add (this);
2274 if (!ev->selected()) {
2275 add_to_selection (ev);
2279 /* find end of latest note selected, select all between that and the start of "ev" */
2281 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2282 Evoral::MusicalTime latest = Evoral::MusicalTime();
2284 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2285 if ((*i)->note()->end_time() > latest) {
2286 latest = (*i)->note()->end_time();
2288 if ((*i)->note()->time() < earliest) {
2289 earliest = (*i)->note()->time();
2293 if (ev->note()->end_time() > latest) {
2294 latest = ev->note()->end_time();
2297 if (ev->note()->time() < earliest) {
2298 earliest = ev->note()->time();
2301 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2303 /* find notes entirely within OR spanning the earliest..latest range */
2305 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2306 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2307 add_to_selection (*i);
2315 MidiRegionView::note_deselected(NoteBase* ev)
2317 remove_from_selection (ev);
2321 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2323 PublicEditor& editor = trackview.editor();
2325 // Convert to local coordinates
2326 const framepos_t p = _region->position();
2327 const double y = midi_view()->y_position();
2328 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2329 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2330 const double y0 = max(0.0, gy0 - y);
2331 const double y1 = max(0.0, gy1 - y);
2333 // TODO: Make this faster by storing the last updated selection rect, and only
2334 // adjusting things that are in the area that appears/disappeared.
2335 // We probably need a tree to be able to find events in O(log(n)) time.
2337 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2338 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2339 // Rectangles intersect
2340 if (!(*i)->selected()) {
2341 add_to_selection (*i);
2343 } else if ((*i)->selected() && !extend) {
2344 // Rectangles do not intersect
2345 remove_from_selection (*i);
2349 typedef RouteTimeAxisView::AutomationTracks ATracks;
2350 typedef std::list<Selectable*> Selectables;
2352 /* Add control points to selection. */
2353 const ATracks& atracks = midi_view()->automation_tracks();
2354 Selectables selectables;
2355 editor.get_selection().clear_points();
2356 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2357 a->second->get_selectables(start, end, gy0, gy1, selectables);
2358 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2359 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2361 editor.get_selection().add(cp);
2364 a->second->set_selected_points(editor.get_selection().points);
2369 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2375 // TODO: Make this faster by storing the last updated selection rect, and only
2376 // adjusting things that are in the area that appears/disappeared.
2377 // We probably need a tree to be able to find events in O(log(n)) time.
2379 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2380 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2381 // within y- (note-) range
2382 if (!(*i)->selected()) {
2383 add_to_selection (*i);
2385 } else if ((*i)->selected() && !extend) {
2386 remove_from_selection (*i);
2392 MidiRegionView::remove_from_selection (NoteBase* ev)
2394 Selection::iterator i = _selection.find (ev);
2396 if (i != _selection.end()) {
2397 _selection.erase (i);
2398 if (_selection.empty() && _grabbed_keyboard) {
2400 Keyboard::magic_widget_drop_focus();
2401 _grabbed_keyboard = false;
2405 ev->set_selected (false);
2406 ev->hide_velocity ();
2408 if (_selection.empty()) {
2409 PublicEditor& editor (trackview.editor());
2410 editor.get_selection().remove (this);
2415 MidiRegionView::add_to_selection (NoteBase* ev)
2417 const bool selection_was_empty = _selection.empty();
2419 if (_selection.insert (ev).second) {
2420 ev->set_selected (true);
2421 start_playing_midi_note ((ev)->note());
2422 if (selection_was_empty && _entered) {
2423 // Grab keyboard for moving notes with arrow keys
2424 Keyboard::magic_widget_grab_focus();
2425 _grabbed_keyboard = true;
2429 if (selection_was_empty) {
2430 PublicEditor& editor (trackview.editor());
2431 editor.get_selection().add (this);
2436 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2438 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2439 PossibleChord to_play;
2440 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2442 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2443 if ((*i)->note()->time() < earliest) {
2444 earliest = (*i)->note()->time();
2448 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2449 if ((*i)->note()->time() == earliest) {
2450 to_play.push_back ((*i)->note());
2452 (*i)->move_event(dx, dy);
2455 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2457 if (to_play.size() > 1) {
2459 PossibleChord shifted;
2461 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2462 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2463 moved_note->set_note (moved_note->note() + cumulative_dy);
2464 shifted.push_back (moved_note);
2467 start_playing_midi_chord (shifted);
2469 } else if (!to_play.empty()) {
2471 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2472 moved_note->set_note (moved_note->note() + cumulative_dy);
2473 start_playing_midi_note (moved_note);
2479 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2481 uint8_t lowest_note_in_selection = 127;
2482 uint8_t highest_note_in_selection = 0;
2483 uint8_t highest_note_difference = 0;
2485 // find highest and lowest notes first
2487 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2488 uint8_t pitch = (*i)->note()->note();
2489 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2490 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2494 cerr << "dnote: " << (int) dnote << endl;
2495 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2496 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2497 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2498 << int(highest_note_in_selection) << endl;
2499 cerr << "selection size: " << _selection.size() << endl;
2500 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2503 // Make sure the note pitch does not exceed the MIDI standard range
2504 if (highest_note_in_selection + dnote > 127) {
2505 highest_note_difference = highest_note_in_selection - 127;
2508 start_note_diff_command (_("move notes"));
2510 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2512 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2513 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2519 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2521 uint8_t original_pitch = (*i)->note()->note();
2522 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2524 // keep notes in standard midi range
2525 clamp_to_0_127(new_pitch);
2527 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2528 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2530 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2535 // care about notes being moved beyond the upper/lower bounds on the canvas
2536 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2537 highest_note_in_selection > midi_stream_view()->highest_note()) {
2538 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2542 /** @param x Pixel relative to the region position.
2543 * @return Snapped frame relative to the region position.
2546 MidiRegionView::snap_pixel_to_sample(double x)
2548 PublicEditor& editor (trackview.editor());
2549 return snap_frame_to_frame (editor.pixel_to_sample (x));
2552 /** @param x Pixel relative to the region position.
2553 * @return Snapped pixel relative to the region position.
2556 MidiRegionView::snap_to_pixel(double x)
2558 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2562 MidiRegionView::get_position_pixels()
2564 framepos_t region_frame = get_position();
2565 return trackview.editor().sample_to_pixel(region_frame);
2569 MidiRegionView::get_end_position_pixels()
2571 framepos_t frame = get_position() + get_duration ();
2572 return trackview.editor().sample_to_pixel(frame);
2576 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2578 /* the time converter will return the frame corresponding to `beats'
2579 relative to the start of the source. The start of the source
2580 is an implied position given by region->position - region->start
2582 const framepos_t source_start = _region->position() - _region->start();
2583 return source_start + _source_relative_time_converter.to (beats);
2587 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2589 /* the `frames' argument needs to be converted into a frame count
2590 relative to the start of the source before being passed in to the
2593 const framepos_t source_start = _region->position() - _region->start();
2594 return _source_relative_time_converter.from (frames - source_start);
2598 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2600 return _region_relative_time_converter.to(beats);
2604 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2606 return _region_relative_time_converter.from(frames);
2610 MidiRegionView::begin_resizing (bool /*at_front*/)
2612 _resize_data.clear();
2614 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2615 Note *note = dynamic_cast<Note*> (*i);
2617 // only insert CanvasNotes into the map
2619 NoteResizeData *resize_data = new NoteResizeData();
2620 resize_data->note = note;
2622 // create a new SimpleRect from the note which will be the resize preview
2623 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2624 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2626 // calculate the colors: get the color settings
2627 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2628 ARDOUR_UI::config()->color ("midi note selected"),
2631 // make the resize preview notes more transparent and bright
2632 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2634 // calculate color based on note velocity
2635 resize_rect->set_fill_color (UINT_INTERPOLATE(
2636 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2640 resize_rect->set_outline_color (NoteBase::calculate_outline (
2641 ARDOUR_UI::config()->color ("midi note selected")));
2643 resize_data->resize_rect = resize_rect;
2644 _resize_data.push_back(resize_data);
2649 /** Update resizing notes while user drags.
2650 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2651 * @param at_front which end of the note (true == note on, false == note off)
2652 * @param delta_x change in mouse position since the start of the drag
2653 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2654 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2655 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2656 * as the \a primary note.
2659 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2661 bool cursor_set = false;
2663 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2664 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2665 Note* canvas_note = (*i)->note;
2670 current_x = canvas_note->x0() + delta_x;
2672 current_x = primary->x0() + delta_x;
2676 current_x = canvas_note->x1() + delta_x;
2678 current_x = primary->x1() + delta_x;
2682 if (current_x < 0) {
2683 // This works even with snapping because RegionView::snap_frame_to_frame()
2684 // snaps forward if the snapped sample is before the beginning of the region
2687 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2688 current_x = trackview.editor().sample_to_pixel(_region->length());
2692 resize_rect->set_x0 (snap_to_pixel(current_x));
2693 resize_rect->set_x1 (canvas_note->x1());
2695 resize_rect->set_x1 (snap_to_pixel(current_x));
2696 resize_rect->set_x0 (canvas_note->x0());
2700 const double snapped_x = snap_pixel_to_sample (current_x);
2701 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2702 Evoral::MusicalTime len = Evoral::MusicalTime();
2705 if (beats < canvas_note->note()->end_time()) {
2706 len = canvas_note->note()->time() - beats;
2707 len += canvas_note->note()->length();
2710 if (beats >= canvas_note->note()->time()) {
2711 len = beats - canvas_note->note()->time();
2716 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2717 show_verbose_cursor (buf, 0, 0);
2726 /** Finish resizing notes when the user releases the mouse button.
2727 * Parameters the same as for \a update_resizing().
2730 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2732 start_note_diff_command (_("resize notes"));
2734 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2735 Note* canvas_note = (*i)->note;
2736 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2738 /* Get the new x position for this resize, which is in pixels relative
2739 * to the region position.
2746 current_x = canvas_note->x0() + delta_x;
2748 current_x = primary->x0() + delta_x;
2752 current_x = canvas_note->x1() + delta_x;
2754 current_x = primary->x1() + delta_x;
2758 if (current_x < 0) {
2761 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2762 current_x = trackview.editor().sample_to_pixel(_region->length());
2765 /* Convert that to a frame within the source */
2766 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2768 /* and then to beats */
2769 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2771 if (at_front && x_beats < canvas_note->note()->end_time()) {
2772 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2774 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2775 len += canvas_note->note()->length();
2778 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2783 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2786 /* XXX convert to beats */
2787 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2795 _resize_data.clear();
2800 MidiRegionView::abort_resizing ()
2802 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2803 delete (*i)->resize_rect;
2807 _resize_data.clear ();
2811 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2813 uint8_t new_velocity;
2816 new_velocity = event->note()->velocity() + velocity;
2817 clamp_to_0_127(new_velocity);
2819 new_velocity = velocity;
2822 event->set_selected (event->selected()); // change color
2824 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2828 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2833 new_note = event->note()->note() + note;
2838 clamp_to_0_127 (new_note);
2839 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2843 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2845 bool change_start = false;
2846 bool change_length = false;
2847 Evoral::MusicalTime new_start;
2848 Evoral::MusicalTime new_length;
2850 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2852 front_delta: if positive - move the start of the note later in time (shortening it)
2853 if negative - move the start of the note earlier in time (lengthening it)
2855 end_delta: if positive - move the end of the note later in time (lengthening it)
2856 if negative - move the end of the note earlier in time (shortening it)
2859 if (!!front_delta) {
2860 if (front_delta < 0) {
2862 if (event->note()->time() < -front_delta) {
2863 new_start = Evoral::MusicalTime();
2865 new_start = event->note()->time() + front_delta; // moves earlier
2868 /* start moved toward zero, so move the end point out to where it used to be.
2869 Note that front_delta is negative, so this increases the length.
2872 new_length = event->note()->length() - front_delta;
2873 change_start = true;
2874 change_length = true;
2878 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2880 if (new_pos < event->note()->end_time()) {
2881 new_start = event->note()->time() + front_delta;
2882 /* start moved toward the end, so move the end point back to where it used to be */
2883 new_length = event->note()->length() - front_delta;
2884 change_start = true;
2885 change_length = true;
2892 bool can_change = true;
2893 if (end_delta < 0) {
2894 if (event->note()->length() < -end_delta) {
2900 new_length = event->note()->length() + end_delta;
2901 change_length = true;
2906 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2909 if (change_length) {
2910 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2915 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2917 uint8_t new_channel;
2921 if (event->note()->channel() < -chn) {
2924 new_channel = event->note()->channel() + chn;
2927 new_channel = event->note()->channel() + chn;
2930 new_channel = (uint8_t) chn;
2933 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2937 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2939 Evoral::MusicalTime new_time;
2943 if (event->note()->time() < -delta) {
2944 new_time = Evoral::MusicalTime();
2946 new_time = event->note()->time() + delta;
2949 new_time = event->note()->time() + delta;
2955 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2959 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2961 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2965 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2970 if (_selection.empty()) {
2985 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2986 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2992 start_note_diff_command (_("change velocities"));
2994 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2995 Selection::iterator next = i;
2999 if (i == _selection.begin()) {
3000 change_note_velocity (*i, delta, true);
3001 value = (*i)->note()->velocity() + delta;
3003 change_note_velocity (*i, value, false);
3007 change_note_velocity (*i, delta, true);
3016 if (!_selection.empty()) {
3018 snprintf (buf, sizeof (buf), "Vel %d",
3019 (int) (*_selection.begin())->note()->velocity());
3020 show_verbose_cursor (buf, 10, 10);
3026 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3028 if (_selection.empty()) {
3045 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3047 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3051 if ((int8_t) (*i)->note()->note() + delta > 127) {
3058 start_note_diff_command (_("transpose"));
3060 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3061 Selection::iterator next = i;
3063 change_note_note (*i, delta, true);
3071 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3075 delta = Evoral::MusicalTime(1.0/128.0);
3077 /* grab the current grid distance */
3078 delta = get_grid_beats(_region->position());
3086 start_note_diff_command (_("change note lengths"));
3088 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3089 Selection::iterator next = i;
3092 /* note the negation of the delta for start */
3095 (start ? -delta : Evoral::MusicalTime()),
3096 (end ? delta : Evoral::MusicalTime()));
3105 MidiRegionView::nudge_notes (bool forward, bool fine)
3107 if (_selection.empty()) {
3111 /* pick a note as the point along the timeline to get the nudge distance.
3112 its not necessarily the earliest note, so we may want to pull the notes out
3113 into a vector and sort before using the first one.
3116 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3117 Evoral::MusicalTime delta;
3121 /* non-fine, move by 1 bar regardless of snap */
3122 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3124 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3126 /* grid is off - use nudge distance */
3129 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3130 delta = region_frames_to_region_beats (fabs ((double)distance));
3136 framepos_t next_pos = ref_point;
3139 if (max_framepos - 1 < next_pos) {
3143 if (next_pos == 0) {
3149 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3150 const framecnt_t distance = ref_point - next_pos;
3151 delta = region_frames_to_region_beats (fabs ((double)distance));
3162 start_note_diff_command (_("nudge"));
3164 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3165 Selection::iterator next = i;
3167 change_note_time (*i, delta, true);
3175 MidiRegionView::change_channel(uint8_t channel)
3177 start_note_diff_command(_("change channel"));
3178 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3179 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3187 MidiRegionView::note_entered(NoteBase* ev)
3189 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3191 pre_note_enter_cursor = editor->get_canvas_cursor ();
3193 if (_mouse_state == SelectTouchDragging) {
3194 note_selected (ev, true);
3197 show_verbose_cursor (ev->note ());
3201 MidiRegionView::note_left (NoteBase*)
3203 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3205 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3206 (*i)->hide_velocity ();
3209 editor->verbose_cursor()->hide ();
3211 if (pre_note_enter_cursor) {
3212 editor->set_canvas_cursor (pre_note_enter_cursor);
3213 pre_note_enter_cursor = 0;
3218 MidiRegionView::patch_entered (PatchChange* p)
3221 /* XXX should get patch name if we can */
3222 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3223 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3224 << _("Channel ") << ((int) p->patch()->channel() + 1);
3225 show_verbose_cursor (s.str(), 10, 20);
3226 p->item().grab_focus();
3230 MidiRegionView::patch_left (PatchChange *)
3232 trackview.editor().verbose_cursor()->hide ();
3233 /* focus will transfer back via the enter-notify event sent to this
3239 MidiRegionView::sysex_entered (SysEx* p)
3243 // need a way to extract text from p->_flag->_text
3245 // show_verbose_cursor (s.str(), 10, 20);
3246 p->item().grab_focus();
3250 MidiRegionView::sysex_left (SysEx *)
3252 trackview.editor().verbose_cursor()->hide ();
3253 /* focus will transfer back via the enter-notify event sent to this
3259 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3261 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3262 Editing::MouseMode mm = editor->current_mouse_mode();
3263 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3265 if (can_set_cursor) {
3266 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3267 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3268 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3269 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3270 } else if (pre_note_enter_cursor) {
3271 editor->set_canvas_cursor (pre_note_enter_cursor);
3277 MidiRegionView::set_frame_color()
3281 TimeAxisViewItem::set_frame_color ();
3288 f = ARDOUR_UI::config()->color ("selected region base");
3289 } else if (high_enough_for_name || !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3290 f = ARDOUR_UI::config()->color_mod ("midi frame base", "midi frame base");
3295 if (!rect_visible) {
3296 f = UINT_RGBA_CHANGE_A (f, 80);
3299 frame->set_fill_color (f);
3303 MidiRegionView::midi_channel_mode_changed ()
3305 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3306 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3307 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3309 if (mode == ForceChannel) {
3310 mask = 0xFFFF; // Show all notes as active (below)
3313 // Update notes for selection
3314 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3315 (*i)->on_channel_selection_change (mask);
3318 _patch_changes.clear ();
3319 display_patch_changes ();
3323 MidiRegionView::instrument_settings_changed ()
3329 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3331 if (_selection.empty()) {
3335 PublicEditor& editor (trackview.editor());
3339 /* XXX what to do ? */
3343 editor.get_cut_buffer().add (selection_as_cut_buffer());
3351 start_note_diff_command();
3353 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3360 note_diff_remove_note (*i);
3370 MidiRegionView::selection_as_cut_buffer () const
3374 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3375 NoteType* n = (*i)->note().get();
3376 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3379 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3385 /** This method handles undo */
3387 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3389 trackview.session()->begin_reversible_command (Operations::paste);
3391 // Paste notes, if available
3392 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3393 if (m != selection.midi_notes.end()) {
3394 ctx.counts.increase_n_notes();
3395 paste_internal(pos, ctx.count, ctx.times, **m);
3398 // Paste control points to automation children, if available
3399 typedef RouteTimeAxisView::AutomationTracks ATracks;
3400 const ATracks& atracks = midi_view()->automation_tracks();
3401 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3402 a->second->paste(pos, selection, ctx);
3405 trackview.session()->commit_reversible_command ();
3410 /** This method handles undo */
3412 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3418 start_note_diff_command (_("paste"));
3420 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3421 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3422 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3423 const Evoral::MusicalTime duration = last_time - first_time;
3424 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3425 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3426 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3427 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3429 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3432 duration, pos, _region->position(),
3437 for (int n = 0; n < (int) times; ++n) {
3439 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3441 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3442 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3444 /* make all newly added notes selected */
3446 note_diff_add_note (copied_note, true);
3447 end_point = copied_note->end_time();
3451 /* if we pasted past the current end of the region, extend the region */
3453 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3454 framepos_t region_end = _region->position() + _region->length() - 1;
3456 if (end_frame > region_end) {
3458 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3460 _region->clear_changes ();
3461 _region->set_length (end_frame - _region->position());
3462 trackview.session()->add_command (new StatefulDiffCommand (_region));
3468 struct EventNoteTimeEarlyFirstComparator {
3469 bool operator() (NoteBase* a, NoteBase* b) {
3470 return a->note()->time() < b->note()->time();
3475 MidiRegionView::time_sort_events ()
3477 if (!_sort_needed) {
3481 EventNoteTimeEarlyFirstComparator cmp;
3484 _sort_needed = false;
3488 MidiRegionView::goto_next_note (bool add_to_selection)
3490 bool use_next = false;
3492 if (_events.back()->selected()) {
3496 time_sort_events ();
3498 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3499 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3501 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3502 if ((*i)->selected()) {
3505 } else if (use_next) {
3506 if (channel_mask & (1 << (*i)->note()->channel())) {
3507 if (!add_to_selection) {
3510 note_selected (*i, true, false);
3517 /* use the first one */
3519 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3520 unique_select (_events.front());
3525 MidiRegionView::goto_previous_note (bool add_to_selection)
3527 bool use_next = false;
3529 if (_events.front()->selected()) {
3533 time_sort_events ();
3535 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3536 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3538 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3539 if ((*i)->selected()) {
3542 } else if (use_next) {
3543 if (channel_mask & (1 << (*i)->note()->channel())) {
3544 if (!add_to_selection) {
3547 note_selected (*i, true, false);
3554 /* use the last one */
3556 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3557 unique_select (*(_events.rbegin()));
3562 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3564 bool had_selected = false;
3566 time_sort_events ();
3568 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3569 if ((*i)->selected()) {
3570 selected.insert ((*i)->note());
3571 had_selected = true;
3575 if (allow_all_if_none_selected && !had_selected) {
3576 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3577 selected.insert ((*i)->note());
3583 MidiRegionView::update_ghost_note (double x, double y)
3585 x = std::max(0.0, x);
3587 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3592 _note_group->canvas_to_item (x, y);
3594 PublicEditor& editor = trackview.editor ();
3596 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3597 framecnt_t grid_frames;
3598 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3600 /* use region_frames... because we are converting a delta within the region
3603 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3605 /* note that this sets the time of the ghost note in beats relative to
3606 the start of the source; that is how all note times are stored.
3608 _ghost_note->note()->set_time (
3609 std::max(Evoral::MusicalTime(),
3610 absolute_frames_to_source_beats (f + _region->position ())));
3611 _ghost_note->note()->set_length (length);
3612 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3613 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3615 /* the ghost note does not appear in ghost regions, so pass false in here */
3616 update_note (_ghost_note, false);
3618 show_verbose_cursor (_ghost_note->note ());
3622 MidiRegionView::create_ghost_note (double x, double y)
3624 remove_ghost_note ();
3626 boost::shared_ptr<NoteType> g (new NoteType);
3627 _ghost_note = new Note (*this, _note_group, g);
3628 _ghost_note->set_ignore_events (true);
3629 _ghost_note->set_outline_color (0x000000aa);
3630 update_ghost_note (x, y);
3631 _ghost_note->show ();
3633 show_verbose_cursor (_ghost_note->note ());
3637 MidiRegionView::remove_ghost_note ()
3644 MidiRegionView::snap_changed ()
3650 create_ghost_note (_last_ghost_x, _last_ghost_y);
3654 MidiRegionView::drop_down_keys ()
3656 _mouse_state = None;
3660 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3662 /* XXX: This is dead code. What was it for? */
3664 double note = midi_stream_view()->y_to_note(y);
3666 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3668 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3670 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3671 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3672 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3673 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3678 bool add_mrv_selection = false;
3680 if (_selection.empty()) {
3681 add_mrv_selection = true;
3684 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3685 if (_selection.insert (*i).second) {
3686 (*i)->set_selected (true);
3690 if (add_mrv_selection) {
3691 PublicEditor& editor (trackview.editor());
3692 editor.get_selection().add (this);
3697 MidiRegionView::color_handler ()
3699 RegionView::color_handler ();
3701 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3702 (*i)->set_selected ((*i)->selected()); // will change color
3705 /* XXX probably more to do here */
3709 MidiRegionView::enable_display (bool yn)
3711 RegionView::enable_display (yn);
3718 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3720 if (_step_edit_cursor == 0) {
3721 ArdourCanvas::Item* const group = get_canvas_group();
3723 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3724 _step_edit_cursor->set_y0 (0);
3725 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3726 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3727 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3730 move_step_edit_cursor (pos);
3731 _step_edit_cursor->show ();
3735 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3737 _step_edit_cursor_position = pos;
3739 if (_step_edit_cursor) {
3740 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3741 _step_edit_cursor->set_x0 (pixel);
3742 set_step_edit_cursor_width (_step_edit_cursor_width);
3747 MidiRegionView::hide_step_edit_cursor ()
3749 if (_step_edit_cursor) {
3750 _step_edit_cursor->hide ();
3755 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3757 _step_edit_cursor_width = beats;
3759 if (_step_edit_cursor) {
3760 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3764 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3765 * @param w Source that the data will end up in.
3768 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3770 if (!_active_notes) {
3771 /* we aren't actively being recorded to */
3775 boost::shared_ptr<MidiSource> src = w.lock ();
3776 if (!src || src != midi_region()->midi_source()) {
3777 /* recorded data was not destined for our source */
3781 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3783 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3785 framepos_t back = max_framepos;
3787 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3788 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3790 if (ev.is_channel_event()) {
3791 if (get_channel_mode() == FilterChannels) {
3792 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3798 /* convert from session frames to source beats */
3799 Evoral::MusicalTime const time_beats = _source_relative_time_converter.from(ev.time());
3801 if (ev.type() == MIDI_CMD_NOTE_ON) {
3802 boost::shared_ptr<NoteType> note (
3803 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3805 add_note (note, true);
3807 /* fix up our note range */
3808 if (ev.note() < _current_range_min) {
3809 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3810 } else if (ev.note() > _current_range_max) {
3811 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3814 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3815 resolve_note (ev.note (), time_beats);
3821 midi_stream_view()->check_record_layers (region(), back);
3825 MidiRegionView::trim_front_starting ()
3827 /* Reparent the note group to the region view's parent, so that it doesn't change
3828 when the region view is trimmed.
3830 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3831 _temporary_note_group->move (group->position ());
3832 _note_group->reparent (_temporary_note_group);
3836 MidiRegionView::trim_front_ending ()
3838 _note_group->reparent (group);
3839 delete _temporary_note_group;
3840 _temporary_note_group = 0;
3842 if (_region->start() < 0) {
3843 /* Trim drag made start time -ve; fix this */
3844 midi_region()->fix_negative_start ();
3849 MidiRegionView::edit_patch_change (PatchChange* pc)
3851 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3853 int response = d.run();
3856 case Gtk::RESPONSE_ACCEPT:
3858 case Gtk::RESPONSE_REJECT:
3859 delete_patch_change (pc);
3865 change_patch_change (pc->patch(), d.patch ());
3869 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3872 // sysyex object doesn't have a pointer to a sysex event
3873 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3874 // c->remove (sysex->sysex());
3875 // _model->apply_command (*trackview.session(), c);
3877 //_sys_exes.clear ();
3878 // display_sysexes();
3882 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3884 using namespace MIDI::Name;
3888 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3890 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3892 MIDI::Name::PatchPrimaryKey patch_key;
3893 get_patch_key_at(n->time(), n->channel(), patch_key);
3894 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3896 patch_key.bank_number,
3897 patch_key.program_number,
3903 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3905 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3906 (int) n->channel() + 1,
3907 (int) n->velocity());
3909 show_verbose_cursor(buf, 10, 20);
3913 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3915 trackview.editor().verbose_cursor()->set (text);
3916 trackview.editor().verbose_cursor()->show ();
3917 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3920 /** @param p A session framepos.
3921 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3922 * @return p snapped to the grid subdivision underneath it.
3925 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3927 PublicEditor& editor = trackview.editor ();
3929 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3931 grid_frames = region_beats_to_region_frames (grid_beats);
3933 /* Hack so that we always snap to the note that we are over, instead of snapping
3934 to the next one if we're more than halfway through the one we're over.
3936 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3937 p -= grid_frames / 2;
3940 return snap_frame_to_frame (p);
3943 /** Called when the selection has been cleared in any MidiRegionView.
3944 * @param rv MidiRegionView that the selection was cleared in.
3947 MidiRegionView::selection_cleared (MidiRegionView* rv)
3953 /* Clear our selection in sympathy; but don't signal the fact */
3954 clear_selection (false);
3958 MidiRegionView::note_button_release ()
3960 delete _note_player;
3965 MidiRegionView::get_channel_mode () const
3967 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3968 return rtav->midi_track()->get_playback_channel_mode();
3972 MidiRegionView::get_selected_channels () const
3974 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3975 return rtav->midi_track()->get_playback_channel_mask();
3980 MidiRegionView::get_grid_beats(framepos_t pos) const
3982 PublicEditor& editor = trackview.editor();
3983 bool success = false;
3984 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3986 beats = Evoral::MusicalTime(1);