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 (trackview.editor().sample_to_pixel(_region->length()));
1569 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1571 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1575 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1577 if (!route_ui || !route_ui->midi_track()) {
1581 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1585 /* NotePlayer deletes itself */
1589 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1591 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1595 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1597 if (!route_ui || !route_ui->midi_track()) {
1601 delete _note_player;
1602 _note_player = new NotePlayer (route_ui->midi_track ());
1603 _note_player->add (note);
1604 _note_player->on ();
1608 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1610 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1614 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1616 if (!route_ui || !route_ui->midi_track()) {
1620 delete _note_player;
1621 _note_player = new NotePlayer (route_ui->midi_track());
1623 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1624 _note_player->add (*n);
1627 _note_player->on ();
1632 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1634 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1635 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1637 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1638 (note->note() <= midi_stream_view()->highest_note());
1643 /** Update a canvas note's size from its model note.
1644 * @param ev Canvas note to update.
1645 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1648 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1650 boost::shared_ptr<NoteType> note = ev->note();
1651 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1652 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1657 /* trim note display to not overlap the end of its region */
1659 if (note->length() > 0) {
1660 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1661 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1663 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1666 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1668 if (!note->length()) {
1669 if (_active_notes && note->note() < 128) {
1670 // If this note is already active there's a stuck note,
1671 // finish the old note rectangle
1672 if (_active_notes[note->note()]) {
1673 Note* const old_rect = _active_notes[note->note()];
1674 boost::shared_ptr<NoteType> old_note = old_rect->note();
1675 old_rect->set_x1 (x);
1676 old_rect->set_outline_all ();
1678 _active_notes[note->note()] = ev;
1680 /* outline all but right edge */
1681 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1682 ArdourCanvas::Rectangle::TOP|
1683 ArdourCanvas::Rectangle::LEFT|
1684 ArdourCanvas::Rectangle::BOTTOM));
1686 /* outline all edges */
1687 ev->set_outline_all ();
1690 if (update_ghost_regions) {
1691 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1692 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1694 gr->update_note (ev);
1701 MidiRegionView::update_hit (Hit* ev)
1703 boost::shared_ptr<NoteType> note = ev->note();
1705 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1706 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1707 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1708 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1710 ev->set_position (ArdourCanvas::Duple (x, y));
1711 ev->set_height (diamond_size);
1714 /** Add a MIDI note to the view (with length).
1716 * If in sustained mode, notes with length 0 will be considered active
1717 * notes, and resolve_note should be called when the corresponding note off
1718 * event arrives, to properly display the note.
1721 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1723 NoteBase* event = 0;
1725 if (midi_view()->note_mode() == Sustained) {
1727 Note* ev_rect = new Note (*this, _note_group, note);
1729 update_note (ev_rect);
1733 MidiGhostRegion* gr;
1735 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1736 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1737 gr->add_note(ev_rect);
1741 } else if (midi_view()->note_mode() == Percussive) {
1743 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1745 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1747 update_hit (ev_diamond);
1756 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1757 note_selected(event, true);
1760 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1761 event->show_velocity();
1764 event->on_channel_selection_change (get_selected_channels());
1765 _events.push_back(event);
1774 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1775 MidiStreamView* const view = mtv->midi_view();
1777 view->update_note_range (note->note());
1781 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1782 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1784 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1786 /* potentially extend region to hold new note */
1788 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1789 framepos_t region_end = _region->last_frame();
1791 if (end_frame > region_end) {
1792 _region->set_length (end_frame - _region->position());
1795 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1796 MidiStreamView* const view = mtv->midi_view();
1798 view->update_note_range(new_note->note());
1800 _marked_for_selection.clear ();
1803 start_note_diff_command (_("step add"));
1804 note_diff_add_note (new_note, true, false);
1807 // last_step_edit_note = new_note;
1811 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1813 change_note_lengths (false, false, beats, false, true);
1816 /** Add a new patch change flag to the canvas.
1817 * @param patch the patch change to add
1818 * @param the text to display in the flag
1819 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1822 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1824 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1825 const double x = trackview.editor().sample_to_pixel (region_frames);
1827 double const height = midi_stream_view()->contents_height();
1829 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1830 // so we need to do something more sophisticated to keep its color
1831 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1834 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1835 new PatchChange(*this, group,
1842 if (patch_change->item().width() < _pixel_width) {
1843 // Show unless patch change is beyond the region bounds
1844 if (region_frames < 0 || region_frames >= _region->length()) {
1845 patch_change->hide();
1847 patch_change->show();
1850 patch_change->hide ();
1853 _patch_changes.push_back (patch_change);
1856 MIDI::Name::PatchPrimaryKey
1857 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1859 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1862 /// Return true iff @p pc applies to the given time on the given channel.
1864 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1866 return pc->time() <= time && pc->channel() == channel;
1870 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1872 // The earliest event not before time
1873 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1875 // Go backwards until we find the latest PC for this channel, or the start
1876 while (i != _model->patch_changes().begin() &&
1877 (i == _model->patch_changes().end() ||
1878 !patch_applies(*i, time, channel))) {
1882 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1883 key.bank_number = (*i)->bank();
1884 key.program_number = (*i)->program ();
1886 key.bank_number = key.program_number = 0;
1889 if (!key.is_sane()) {
1890 error << string_compose(_("insane MIDI patch key %1:%2"),
1891 key.bank_number, key.program_number) << endmsg;
1896 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1898 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1900 if (pc.patch()->program() != new_patch.program_number) {
1901 c->change_program (pc.patch (), new_patch.program_number);
1904 int const new_bank = new_patch.bank_number;
1905 if (pc.patch()->bank() != new_bank) {
1906 c->change_bank (pc.patch (), new_bank);
1909 _model->apply_command (*trackview.session(), c);
1911 _patch_changes.clear ();
1912 display_patch_changes ();
1916 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1918 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1920 if (old_change->time() != new_change.time()) {
1921 c->change_time (old_change, new_change.time());
1924 if (old_change->channel() != new_change.channel()) {
1925 c->change_channel (old_change, new_change.channel());
1928 if (old_change->program() != new_change.program()) {
1929 c->change_program (old_change, new_change.program());
1932 if (old_change->bank() != new_change.bank()) {
1933 c->change_bank (old_change, new_change.bank());
1936 _model->apply_command (*trackview.session(), c);
1938 _patch_changes.clear ();
1939 display_patch_changes ();
1942 /** Add a patch change to the region.
1943 * @param t Time in frames relative to region position
1944 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1945 * MidiTimeAxisView::get_channel_for_add())
1948 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1950 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1952 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1953 c->add (MidiModel::PatchChangePtr (
1954 new Evoral::PatchChange<Evoral::MusicalTime> (
1955 absolute_frames_to_source_beats (_region->position() + t),
1956 mtv->get_channel_for_add(), patch.program(), patch.bank()
1961 _model->apply_command (*trackview.session(), c);
1963 _patch_changes.clear ();
1964 display_patch_changes ();
1968 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1970 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1971 c->change_time (pc.patch (), t);
1972 _model->apply_command (*trackview.session(), c);
1974 _patch_changes.clear ();
1975 display_patch_changes ();
1979 MidiRegionView::delete_patch_change (PatchChange* pc)
1981 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1982 c->remove (pc->patch ());
1983 _model->apply_command (*trackview.session(), c);
1985 _patch_changes.clear ();
1986 display_patch_changes ();
1990 MidiRegionView::previous_patch (PatchChange& patch)
1992 if (patch.patch()->program() < 127) {
1993 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1994 key.program_number++;
1995 change_patch_change (patch, key);
2000 MidiRegionView::next_patch (PatchChange& patch)
2002 if (patch.patch()->program() > 0) {
2003 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2004 key.program_number--;
2005 change_patch_change (patch, key);
2010 MidiRegionView::next_bank (PatchChange& patch)
2012 if (patch.patch()->program() < 127) {
2013 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2014 if (key.bank_number > 0) {
2016 change_patch_change (patch, key);
2022 MidiRegionView::previous_bank (PatchChange& patch)
2024 if (patch.patch()->program() > 0) {
2025 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2026 if (key.bank_number < 127) {
2028 change_patch_change (patch, key);
2034 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2036 if (_selection.empty()) {
2040 _selection.erase (cne);
2044 MidiRegionView::delete_selection()
2046 if (_selection.empty()) {
2050 start_note_diff_command (_("delete selection"));
2052 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2053 if ((*i)->selected()) {
2054 _note_diff_command->remove((*i)->note());
2064 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2066 start_note_diff_command (_("delete note"));
2067 _note_diff_command->remove (n);
2070 trackview.editor().verbose_cursor()->hide ();
2074 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2076 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2078 Selection::iterator tmp = i;
2081 (*i)->set_selected (false);
2082 (*i)->hide_velocity ();
2083 _selection.erase (i);
2091 if (!ev && _entered) {
2092 // Clearing selection entirely, ungrab keyboard
2093 Keyboard::magic_widget_drop_focus();
2094 _grabbed_keyboard = false;
2097 /* this does not change the status of this regionview w.r.t the editor
2102 SelectionCleared (this); /* EMIT SIGNAL */
2107 MidiRegionView::unique_select(NoteBase* ev)
2109 const bool selection_was_empty = _selection.empty();
2111 clear_selection_except (ev);
2113 /* don't bother with checking to see if we should remove this
2114 regionview from the editor selection, since we're about to add
2115 another note, and thus put/keep this regionview in the editor
2119 if (!ev->selected()) {
2120 add_to_selection (ev);
2121 if (selection_was_empty && _entered) {
2122 // Grab keyboard for moving notes with arrow keys
2123 Keyboard::magic_widget_grab_focus();
2124 _grabbed_keyboard = true;
2130 MidiRegionView::select_all_notes ()
2134 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2135 add_to_selection (*i);
2140 MidiRegionView::select_range (framepos_t start, framepos_t end)
2144 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2145 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2146 if (t >= start && t <= end) {
2147 add_to_selection (*i);
2153 MidiRegionView::invert_selection ()
2155 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2156 if ((*i)->selected()) {
2157 remove_from_selection(*i);
2159 add_to_selection (*i);
2165 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2167 bool have_selection = !_selection.empty();
2168 uint8_t low_note = 127;
2169 uint8_t high_note = 0;
2170 MidiModel::Notes& notes (_model->notes());
2171 _optimization_iterator = _events.begin();
2173 if (extend && !have_selection) {
2177 /* scan existing selection to get note range */
2179 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2180 if ((*i)->note()->note() < low_note) {
2181 low_note = (*i)->note()->note();
2183 if ((*i)->note()->note() > high_note) {
2184 high_note = (*i)->note()->note();
2191 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2192 /* only note previously selected is the one we are
2193 * reselecting. treat this as cancelling the selection.
2200 low_note = min (low_note, notenum);
2201 high_note = max (high_note, notenum);
2204 _no_sound_notes = true;
2206 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2208 boost::shared_ptr<NoteType> note (*n);
2210 bool select = false;
2212 if (((1 << note->channel()) & channel_mask) != 0) {
2214 if ((note->note() >= low_note && note->note() <= high_note)) {
2217 } else if (note->note() == notenum) {
2223 if ((cne = find_canvas_note (note)) != 0) {
2224 // extend is false because we've taken care of it,
2225 // since it extends by time range, not pitch.
2226 note_selected (cne, add, false);
2230 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2234 _no_sound_notes = false;
2238 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2240 MidiModel::Notes& notes (_model->notes());
2241 _optimization_iterator = _events.begin();
2243 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2245 boost::shared_ptr<NoteType> note (*n);
2248 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2249 if ((cne = find_canvas_note (note)) != 0) {
2250 if (cne->selected()) {
2251 note_deselected (cne);
2253 note_selected (cne, true, false);
2261 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2264 clear_selection_except (ev);
2265 if (!_selection.empty()) {
2266 PublicEditor& editor (trackview.editor());
2267 editor.get_selection().add (this);
2273 if (!ev->selected()) {
2274 add_to_selection (ev);
2278 /* find end of latest note selected, select all between that and the start of "ev" */
2280 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2281 Evoral::MusicalTime latest = Evoral::MusicalTime();
2283 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2284 if ((*i)->note()->end_time() > latest) {
2285 latest = (*i)->note()->end_time();
2287 if ((*i)->note()->time() < earliest) {
2288 earliest = (*i)->note()->time();
2292 if (ev->note()->end_time() > latest) {
2293 latest = ev->note()->end_time();
2296 if (ev->note()->time() < earliest) {
2297 earliest = ev->note()->time();
2300 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2302 /* find notes entirely within OR spanning the earliest..latest range */
2304 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2305 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2306 add_to_selection (*i);
2314 MidiRegionView::note_deselected(NoteBase* ev)
2316 remove_from_selection (ev);
2320 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2322 PublicEditor& editor = trackview.editor();
2324 // Convert to local coordinates
2325 const framepos_t p = _region->position();
2326 const double y = midi_view()->y_position();
2327 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2328 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2329 const double y0 = max(0.0, gy0 - y);
2330 const double y1 = max(0.0, gy1 - y);
2332 // TODO: Make this faster by storing the last updated selection rect, and only
2333 // adjusting things that are in the area that appears/disappeared.
2334 // We probably need a tree to be able to find events in O(log(n)) time.
2336 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2337 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2338 // Rectangles intersect
2339 if (!(*i)->selected()) {
2340 add_to_selection (*i);
2342 } else if ((*i)->selected() && !extend) {
2343 // Rectangles do not intersect
2344 remove_from_selection (*i);
2348 typedef RouteTimeAxisView::AutomationTracks ATracks;
2349 typedef std::list<Selectable*> Selectables;
2351 /* Add control points to selection. */
2352 const ATracks& atracks = midi_view()->automation_tracks();
2353 Selectables selectables;
2354 editor.get_selection().clear_points();
2355 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2356 a->second->get_selectables(start, end, gy0, gy1, selectables);
2357 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2358 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2360 editor.get_selection().add(cp);
2363 a->second->set_selected_points(editor.get_selection().points);
2368 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2374 // TODO: Make this faster by storing the last updated selection rect, and only
2375 // adjusting things that are in the area that appears/disappeared.
2376 // We probably need a tree to be able to find events in O(log(n)) time.
2378 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2379 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2380 // within y- (note-) range
2381 if (!(*i)->selected()) {
2382 add_to_selection (*i);
2384 } else if ((*i)->selected() && !extend) {
2385 remove_from_selection (*i);
2391 MidiRegionView::remove_from_selection (NoteBase* ev)
2393 Selection::iterator i = _selection.find (ev);
2395 if (i != _selection.end()) {
2396 _selection.erase (i);
2397 if (_selection.empty() && _grabbed_keyboard) {
2399 Keyboard::magic_widget_drop_focus();
2400 _grabbed_keyboard = false;
2404 ev->set_selected (false);
2405 ev->hide_velocity ();
2407 if (_selection.empty()) {
2408 PublicEditor& editor (trackview.editor());
2409 editor.get_selection().remove (this);
2414 MidiRegionView::add_to_selection (NoteBase* ev)
2416 const bool selection_was_empty = _selection.empty();
2418 if (_selection.insert (ev).second) {
2419 ev->set_selected (true);
2420 start_playing_midi_note ((ev)->note());
2421 if (selection_was_empty && _entered) {
2422 // Grab keyboard for moving notes with arrow keys
2423 Keyboard::magic_widget_grab_focus();
2424 _grabbed_keyboard = true;
2428 if (selection_was_empty) {
2429 PublicEditor& editor (trackview.editor());
2430 editor.get_selection().add (this);
2435 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2437 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2438 PossibleChord to_play;
2439 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2441 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2442 if ((*i)->note()->time() < earliest) {
2443 earliest = (*i)->note()->time();
2447 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2448 if ((*i)->note()->time() == earliest) {
2449 to_play.push_back ((*i)->note());
2451 (*i)->move_event(dx, dy);
2454 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2456 if (to_play.size() > 1) {
2458 PossibleChord shifted;
2460 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2461 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2462 moved_note->set_note (moved_note->note() + cumulative_dy);
2463 shifted.push_back (moved_note);
2466 start_playing_midi_chord (shifted);
2468 } else if (!to_play.empty()) {
2470 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2471 moved_note->set_note (moved_note->note() + cumulative_dy);
2472 start_playing_midi_note (moved_note);
2478 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2480 uint8_t lowest_note_in_selection = 127;
2481 uint8_t highest_note_in_selection = 0;
2482 uint8_t highest_note_difference = 0;
2484 // find highest and lowest notes first
2486 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2487 uint8_t pitch = (*i)->note()->note();
2488 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2489 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2493 cerr << "dnote: " << (int) dnote << endl;
2494 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2495 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2496 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2497 << int(highest_note_in_selection) << endl;
2498 cerr << "selection size: " << _selection.size() << endl;
2499 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2502 // Make sure the note pitch does not exceed the MIDI standard range
2503 if (highest_note_in_selection + dnote > 127) {
2504 highest_note_difference = highest_note_in_selection - 127;
2507 start_note_diff_command (_("move notes"));
2509 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2511 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2512 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2518 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2520 uint8_t original_pitch = (*i)->note()->note();
2521 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2523 // keep notes in standard midi range
2524 clamp_to_0_127(new_pitch);
2526 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2527 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2529 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2534 // care about notes being moved beyond the upper/lower bounds on the canvas
2535 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2536 highest_note_in_selection > midi_stream_view()->highest_note()) {
2537 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2541 /** @param x Pixel relative to the region position.
2542 * @return Snapped frame relative to the region position.
2545 MidiRegionView::snap_pixel_to_sample(double x)
2547 PublicEditor& editor (trackview.editor());
2548 return snap_frame_to_frame (editor.pixel_to_sample (x));
2551 /** @param x Pixel relative to the region position.
2552 * @return Snapped pixel relative to the region position.
2555 MidiRegionView::snap_to_pixel(double x)
2557 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2561 MidiRegionView::get_position_pixels()
2563 framepos_t region_frame = get_position();
2564 return trackview.editor().sample_to_pixel(region_frame);
2568 MidiRegionView::get_end_position_pixels()
2570 framepos_t frame = get_position() + get_duration ();
2571 return trackview.editor().sample_to_pixel(frame);
2575 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2577 /* the time converter will return the frame corresponding to `beats'
2578 relative to the start of the source. The start of the source
2579 is an implied position given by region->position - region->start
2581 const framepos_t source_start = _region->position() - _region->start();
2582 return source_start + _source_relative_time_converter.to (beats);
2586 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2588 /* the `frames' argument needs to be converted into a frame count
2589 relative to the start of the source before being passed in to the
2592 const framepos_t source_start = _region->position() - _region->start();
2593 return _source_relative_time_converter.from (frames - source_start);
2597 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2599 return _region_relative_time_converter.to(beats);
2603 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2605 return _region_relative_time_converter.from(frames);
2609 MidiRegionView::begin_resizing (bool /*at_front*/)
2611 _resize_data.clear();
2613 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2614 Note *note = dynamic_cast<Note*> (*i);
2616 // only insert CanvasNotes into the map
2618 NoteResizeData *resize_data = new NoteResizeData();
2619 resize_data->note = note;
2621 // create a new SimpleRect from the note which will be the resize preview
2622 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2623 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2625 // calculate the colors: get the color settings
2626 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2627 ARDOUR_UI::config()->color ("midi note selected"),
2630 // make the resize preview notes more transparent and bright
2631 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2633 // calculate color based on note velocity
2634 resize_rect->set_fill_color (UINT_INTERPOLATE(
2635 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2639 resize_rect->set_outline_color (NoteBase::calculate_outline (
2640 ARDOUR_UI::config()->color ("midi note selected")));
2642 resize_data->resize_rect = resize_rect;
2643 _resize_data.push_back(resize_data);
2648 /** Update resizing notes while user drags.
2649 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2650 * @param at_front which end of the note (true == note on, false == note off)
2651 * @param delta_x change in mouse position since the start of the drag
2652 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2653 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2654 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2655 * as the \a primary note.
2658 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2660 bool cursor_set = false;
2662 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2663 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2664 Note* canvas_note = (*i)->note;
2669 current_x = canvas_note->x0() + delta_x;
2671 current_x = primary->x0() + delta_x;
2675 current_x = canvas_note->x1() + delta_x;
2677 current_x = primary->x1() + delta_x;
2681 if (current_x < 0) {
2682 // This works even with snapping because RegionView::snap_frame_to_frame()
2683 // snaps forward if the snapped sample is before the beginning of the region
2686 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2687 current_x = trackview.editor().sample_to_pixel(_region->length());
2691 resize_rect->set_x0 (snap_to_pixel(current_x));
2692 resize_rect->set_x1 (canvas_note->x1());
2694 resize_rect->set_x1 (snap_to_pixel(current_x));
2695 resize_rect->set_x0 (canvas_note->x0());
2699 const double snapped_x = snap_pixel_to_sample (current_x);
2700 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2701 Evoral::MusicalTime len = Evoral::MusicalTime();
2704 if (beats < canvas_note->note()->end_time()) {
2705 len = canvas_note->note()->time() - beats;
2706 len += canvas_note->note()->length();
2709 if (beats >= canvas_note->note()->time()) {
2710 len = beats - canvas_note->note()->time();
2715 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2716 show_verbose_cursor (buf, 0, 0);
2725 /** Finish resizing notes when the user releases the mouse button.
2726 * Parameters the same as for \a update_resizing().
2729 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2731 start_note_diff_command (_("resize notes"));
2733 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2734 Note* canvas_note = (*i)->note;
2735 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2737 /* Get the new x position for this resize, which is in pixels relative
2738 * to the region position.
2745 current_x = canvas_note->x0() + delta_x;
2747 current_x = primary->x0() + delta_x;
2751 current_x = canvas_note->x1() + delta_x;
2753 current_x = primary->x1() + delta_x;
2757 if (current_x < 0) {
2760 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2761 current_x = trackview.editor().sample_to_pixel(_region->length());
2764 /* Convert that to a frame within the source */
2765 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2767 /* and then to beats */
2768 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2770 if (at_front && x_beats < canvas_note->note()->end_time()) {
2771 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2773 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2774 len += canvas_note->note()->length();
2777 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2782 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2785 /* XXX convert to beats */
2786 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2794 _resize_data.clear();
2799 MidiRegionView::abort_resizing ()
2801 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2802 delete (*i)->resize_rect;
2806 _resize_data.clear ();
2810 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2812 uint8_t new_velocity;
2815 new_velocity = event->note()->velocity() + velocity;
2816 clamp_to_0_127(new_velocity);
2818 new_velocity = velocity;
2821 event->set_selected (event->selected()); // change color
2823 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2827 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2832 new_note = event->note()->note() + note;
2837 clamp_to_0_127 (new_note);
2838 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2842 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2844 bool change_start = false;
2845 bool change_length = false;
2846 Evoral::MusicalTime new_start;
2847 Evoral::MusicalTime new_length;
2849 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2851 front_delta: if positive - move the start of the note later in time (shortening it)
2852 if negative - move the start of the note earlier in time (lengthening it)
2854 end_delta: if positive - move the end of the note later in time (lengthening it)
2855 if negative - move the end of the note earlier in time (shortening it)
2858 if (!!front_delta) {
2859 if (front_delta < 0) {
2861 if (event->note()->time() < -front_delta) {
2862 new_start = Evoral::MusicalTime();
2864 new_start = event->note()->time() + front_delta; // moves earlier
2867 /* start moved toward zero, so move the end point out to where it used to be.
2868 Note that front_delta is negative, so this increases the length.
2871 new_length = event->note()->length() - front_delta;
2872 change_start = true;
2873 change_length = true;
2877 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2879 if (new_pos < event->note()->end_time()) {
2880 new_start = event->note()->time() + front_delta;
2881 /* start moved toward the end, so move the end point back to where it used to be */
2882 new_length = event->note()->length() - front_delta;
2883 change_start = true;
2884 change_length = true;
2891 bool can_change = true;
2892 if (end_delta < 0) {
2893 if (event->note()->length() < -end_delta) {
2899 new_length = event->note()->length() + end_delta;
2900 change_length = true;
2905 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2908 if (change_length) {
2909 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2914 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2916 uint8_t new_channel;
2920 if (event->note()->channel() < -chn) {
2923 new_channel = event->note()->channel() + chn;
2926 new_channel = event->note()->channel() + chn;
2929 new_channel = (uint8_t) chn;
2932 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2936 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2938 Evoral::MusicalTime new_time;
2942 if (event->note()->time() < -delta) {
2943 new_time = Evoral::MusicalTime();
2945 new_time = event->note()->time() + delta;
2948 new_time = event->note()->time() + delta;
2954 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2958 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2960 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2964 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2969 if (_selection.empty()) {
2984 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2985 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2991 start_note_diff_command (_("change velocities"));
2993 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2994 Selection::iterator next = i;
2998 if (i == _selection.begin()) {
2999 change_note_velocity (*i, delta, true);
3000 value = (*i)->note()->velocity() + delta;
3002 change_note_velocity (*i, value, false);
3006 change_note_velocity (*i, delta, true);
3015 if (!_selection.empty()) {
3017 snprintf (buf, sizeof (buf), "Vel %d",
3018 (int) (*_selection.begin())->note()->velocity());
3019 show_verbose_cursor (buf, 10, 10);
3025 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3027 if (_selection.empty()) {
3044 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3046 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3050 if ((int8_t) (*i)->note()->note() + delta > 127) {
3057 start_note_diff_command (_("transpose"));
3059 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3060 Selection::iterator next = i;
3062 change_note_note (*i, delta, true);
3070 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3074 delta = Evoral::MusicalTime(1.0/128.0);
3076 /* grab the current grid distance */
3077 delta = get_grid_beats(_region->position());
3085 start_note_diff_command (_("change note lengths"));
3087 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3088 Selection::iterator next = i;
3091 /* note the negation of the delta for start */
3094 (start ? -delta : Evoral::MusicalTime()),
3095 (end ? delta : Evoral::MusicalTime()));
3104 MidiRegionView::nudge_notes (bool forward, bool fine)
3106 if (_selection.empty()) {
3110 /* pick a note as the point along the timeline to get the nudge distance.
3111 its not necessarily the earliest note, so we may want to pull the notes out
3112 into a vector and sort before using the first one.
3115 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3116 Evoral::MusicalTime delta;
3120 /* non-fine, move by 1 bar regardless of snap */
3121 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3123 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3125 /* grid is off - use nudge distance */
3128 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3129 delta = region_frames_to_region_beats (fabs ((double)distance));
3135 framepos_t next_pos = ref_point;
3138 if (max_framepos - 1 < next_pos) {
3142 if (next_pos == 0) {
3148 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3149 const framecnt_t distance = ref_point - next_pos;
3150 delta = region_frames_to_region_beats (fabs ((double)distance));
3161 start_note_diff_command (_("nudge"));
3163 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3164 Selection::iterator next = i;
3166 change_note_time (*i, delta, true);
3174 MidiRegionView::change_channel(uint8_t channel)
3176 start_note_diff_command(_("change channel"));
3177 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3178 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3186 MidiRegionView::note_entered(NoteBase* ev)
3188 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3190 pre_note_enter_cursor = editor->get_canvas_cursor ();
3192 if (_mouse_state == SelectTouchDragging) {
3193 note_selected (ev, true);
3196 show_verbose_cursor (ev->note ());
3200 MidiRegionView::note_left (NoteBase*)
3202 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3204 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3205 (*i)->hide_velocity ();
3208 editor->verbose_cursor()->hide ();
3210 if (pre_note_enter_cursor) {
3211 editor->set_canvas_cursor (pre_note_enter_cursor);
3212 pre_note_enter_cursor = 0;
3217 MidiRegionView::patch_entered (PatchChange* p)
3220 /* XXX should get patch name if we can */
3221 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3222 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3223 << _("Channel ") << ((int) p->patch()->channel() + 1);
3224 show_verbose_cursor (s.str(), 10, 20);
3225 p->item().grab_focus();
3229 MidiRegionView::patch_left (PatchChange *)
3231 trackview.editor().verbose_cursor()->hide ();
3232 /* focus will transfer back via the enter-notify event sent to this
3238 MidiRegionView::sysex_entered (SysEx* p)
3242 // need a way to extract text from p->_flag->_text
3244 // show_verbose_cursor (s.str(), 10, 20);
3245 p->item().grab_focus();
3249 MidiRegionView::sysex_left (SysEx *)
3251 trackview.editor().verbose_cursor()->hide ();
3252 /* focus will transfer back via the enter-notify event sent to this
3258 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3260 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3261 Editing::MouseMode mm = editor->current_mouse_mode();
3262 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3264 if (can_set_cursor) {
3265 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3266 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3267 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3268 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3269 } else if (pre_note_enter_cursor) {
3270 editor->set_canvas_cursor (pre_note_enter_cursor);
3276 MidiRegionView::set_frame_color()
3280 TimeAxisViewItem::set_frame_color ();
3287 f = ARDOUR_UI::config()->color ("selected region base");
3288 } else if (high_enough_for_name || !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3289 f = ARDOUR_UI::config()->color_mod ("midi frame base", "midi frame base");
3294 if (!rect_visible) {
3295 f = UINT_RGBA_CHANGE_A (f, 80);
3298 frame->set_fill_color (f);
3302 MidiRegionView::midi_channel_mode_changed ()
3304 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3305 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3306 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3308 if (mode == ForceChannel) {
3309 mask = 0xFFFF; // Show all notes as active (below)
3312 // Update notes for selection
3313 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3314 (*i)->on_channel_selection_change (mask);
3317 _patch_changes.clear ();
3318 display_patch_changes ();
3322 MidiRegionView::instrument_settings_changed ()
3328 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3330 if (_selection.empty()) {
3334 PublicEditor& editor (trackview.editor());
3338 /* XXX what to do ? */
3342 editor.get_cut_buffer().add (selection_as_cut_buffer());
3350 start_note_diff_command();
3352 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3359 note_diff_remove_note (*i);
3369 MidiRegionView::selection_as_cut_buffer () const
3373 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3374 NoteType* n = (*i)->note().get();
3375 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3378 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3384 /** This method handles undo */
3386 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3388 trackview.session()->begin_reversible_command (Operations::paste);
3390 // Paste notes, if available
3391 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3392 if (m != selection.midi_notes.end()) {
3393 ctx.counts.increase_n_notes();
3394 paste_internal(pos, ctx.count, ctx.times, **m);
3397 // Paste control points to automation children, if available
3398 typedef RouteTimeAxisView::AutomationTracks ATracks;
3399 const ATracks& atracks = midi_view()->automation_tracks();
3400 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3401 a->second->paste(pos, selection, ctx);
3404 trackview.session()->commit_reversible_command ();
3409 /** This method handles undo */
3411 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3417 start_note_diff_command (_("paste"));
3419 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3420 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3421 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3422 const Evoral::MusicalTime duration = last_time - first_time;
3423 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3424 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3425 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3426 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3428 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3431 duration, pos, _region->position(),
3436 for (int n = 0; n < (int) times; ++n) {
3438 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3440 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3441 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3443 /* make all newly added notes selected */
3445 note_diff_add_note (copied_note, true);
3446 end_point = copied_note->end_time();
3450 /* if we pasted past the current end of the region, extend the region */
3452 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3453 framepos_t region_end = _region->position() + _region->length() - 1;
3455 if (end_frame > region_end) {
3457 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3459 _region->clear_changes ();
3460 _region->set_length (end_frame - _region->position());
3461 trackview.session()->add_command (new StatefulDiffCommand (_region));
3467 struct EventNoteTimeEarlyFirstComparator {
3468 bool operator() (NoteBase* a, NoteBase* b) {
3469 return a->note()->time() < b->note()->time();
3474 MidiRegionView::time_sort_events ()
3476 if (!_sort_needed) {
3480 EventNoteTimeEarlyFirstComparator cmp;
3483 _sort_needed = false;
3487 MidiRegionView::goto_next_note (bool add_to_selection)
3489 bool use_next = false;
3491 if (_events.back()->selected()) {
3495 time_sort_events ();
3497 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3498 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3500 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3501 if ((*i)->selected()) {
3504 } else if (use_next) {
3505 if (channel_mask & (1 << (*i)->note()->channel())) {
3506 if (!add_to_selection) {
3509 note_selected (*i, true, false);
3516 /* use the first one */
3518 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3519 unique_select (_events.front());
3524 MidiRegionView::goto_previous_note (bool add_to_selection)
3526 bool use_next = false;
3528 if (_events.front()->selected()) {
3532 time_sort_events ();
3534 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3535 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3537 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3538 if ((*i)->selected()) {
3541 } else if (use_next) {
3542 if (channel_mask & (1 << (*i)->note()->channel())) {
3543 if (!add_to_selection) {
3546 note_selected (*i, true, false);
3553 /* use the last one */
3555 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3556 unique_select (*(_events.rbegin()));
3561 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3563 bool had_selected = false;
3565 time_sort_events ();
3567 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3568 if ((*i)->selected()) {
3569 selected.insert ((*i)->note());
3570 had_selected = true;
3574 if (allow_all_if_none_selected && !had_selected) {
3575 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3576 selected.insert ((*i)->note());
3582 MidiRegionView::update_ghost_note (double x, double y)
3584 x = std::max(0.0, x);
3586 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3591 _note_group->canvas_to_item (x, y);
3593 PublicEditor& editor = trackview.editor ();
3595 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3596 framecnt_t grid_frames;
3597 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3599 /* use region_frames... because we are converting a delta within the region
3602 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3604 /* note that this sets the time of the ghost note in beats relative to
3605 the start of the source; that is how all note times are stored.
3607 _ghost_note->note()->set_time (
3608 std::max(Evoral::MusicalTime(),
3609 absolute_frames_to_source_beats (f + _region->position ())));
3610 _ghost_note->note()->set_length (length);
3611 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3612 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3614 /* the ghost note does not appear in ghost regions, so pass false in here */
3615 update_note (_ghost_note, false);
3617 show_verbose_cursor (_ghost_note->note ());
3621 MidiRegionView::create_ghost_note (double x, double y)
3623 remove_ghost_note ();
3625 boost::shared_ptr<NoteType> g (new NoteType);
3626 _ghost_note = new Note (*this, _note_group, g);
3627 _ghost_note->set_ignore_events (true);
3628 _ghost_note->set_outline_color (0x000000aa);
3629 update_ghost_note (x, y);
3630 _ghost_note->show ();
3632 show_verbose_cursor (_ghost_note->note ());
3636 MidiRegionView::remove_ghost_note ()
3643 MidiRegionView::snap_changed ()
3649 create_ghost_note (_last_ghost_x, _last_ghost_y);
3653 MidiRegionView::drop_down_keys ()
3655 _mouse_state = None;
3659 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3661 /* XXX: This is dead code. What was it for? */
3663 double note = midi_stream_view()->y_to_note(y);
3665 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3667 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3669 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3670 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3671 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3672 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3677 bool add_mrv_selection = false;
3679 if (_selection.empty()) {
3680 add_mrv_selection = true;
3683 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3684 if (_selection.insert (*i).second) {
3685 (*i)->set_selected (true);
3689 if (add_mrv_selection) {
3690 PublicEditor& editor (trackview.editor());
3691 editor.get_selection().add (this);
3696 MidiRegionView::color_handler ()
3698 RegionView::color_handler ();
3700 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3701 (*i)->set_selected ((*i)->selected()); // will change color
3704 /* XXX probably more to do here */
3708 MidiRegionView::enable_display (bool yn)
3710 RegionView::enable_display (yn);
3717 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3719 if (_step_edit_cursor == 0) {
3720 ArdourCanvas::Item* const group = get_canvas_group();
3722 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3723 _step_edit_cursor->set_y0 (0);
3724 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3725 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3726 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3729 move_step_edit_cursor (pos);
3730 _step_edit_cursor->show ();
3734 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3736 _step_edit_cursor_position = pos;
3738 if (_step_edit_cursor) {
3739 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3740 _step_edit_cursor->set_x0 (pixel);
3741 set_step_edit_cursor_width (_step_edit_cursor_width);
3746 MidiRegionView::hide_step_edit_cursor ()
3748 if (_step_edit_cursor) {
3749 _step_edit_cursor->hide ();
3754 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3756 _step_edit_cursor_width = beats;
3758 if (_step_edit_cursor) {
3759 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3763 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3764 * @param w Source that the data will end up in.
3767 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3769 if (!_active_notes) {
3770 /* we aren't actively being recorded to */
3774 boost::shared_ptr<MidiSource> src = w.lock ();
3775 if (!src || src != midi_region()->midi_source()) {
3776 /* recorded data was not destined for our source */
3780 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3782 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3784 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3786 framepos_t back = max_framepos;
3788 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3789 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3791 if (ev.is_channel_event()) {
3792 if (get_channel_mode() == FilterChannels) {
3793 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3799 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3800 frames from the start of the source, and so time_beats is in terms of the
3804 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3806 if (ev.type() == MIDI_CMD_NOTE_ON) {
3807 boost::shared_ptr<NoteType> note (
3808 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3810 add_note (note, true);
3812 /* fix up our note range */
3813 if (ev.note() < _current_range_min) {
3814 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3815 } else if (ev.note() > _current_range_max) {
3816 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3819 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3820 resolve_note (ev.note (), time_beats);
3826 midi_stream_view()->check_record_layers (region(), back);
3830 MidiRegionView::trim_front_starting ()
3832 /* Reparent the note group to the region view's parent, so that it doesn't change
3833 when the region view is trimmed.
3835 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3836 _temporary_note_group->move (group->position ());
3837 _note_group->reparent (_temporary_note_group);
3841 MidiRegionView::trim_front_ending ()
3843 _note_group->reparent (group);
3844 delete _temporary_note_group;
3845 _temporary_note_group = 0;
3847 if (_region->start() < 0) {
3848 /* Trim drag made start time -ve; fix this */
3849 midi_region()->fix_negative_start ();
3854 MidiRegionView::edit_patch_change (PatchChange* pc)
3856 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3858 int response = d.run();
3861 case Gtk::RESPONSE_ACCEPT:
3863 case Gtk::RESPONSE_REJECT:
3864 delete_patch_change (pc);
3870 change_patch_change (pc->patch(), d.patch ());
3874 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3877 // sysyex object doesn't have a pointer to a sysex event
3878 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3879 // c->remove (sysex->sysex());
3880 // _model->apply_command (*trackview.session(), c);
3882 //_sys_exes.clear ();
3883 // display_sysexes();
3887 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3889 using namespace MIDI::Name;
3893 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3895 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3897 MIDI::Name::PatchPrimaryKey patch_key;
3898 get_patch_key_at(n->time(), n->channel(), patch_key);
3899 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3901 patch_key.bank_number,
3902 patch_key.program_number,
3908 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3910 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3911 (int) n->channel() + 1,
3912 (int) n->velocity());
3914 show_verbose_cursor(buf, 10, 20);
3918 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3920 trackview.editor().verbose_cursor()->set (text);
3921 trackview.editor().verbose_cursor()->show ();
3922 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3925 /** @param p A session framepos.
3926 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3927 * @return p snapped to the grid subdivision underneath it.
3930 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3932 PublicEditor& editor = trackview.editor ();
3934 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3936 grid_frames = region_beats_to_region_frames (grid_beats);
3938 /* Hack so that we always snap to the note that we are over, instead of snapping
3939 to the next one if we're more than halfway through the one we're over.
3941 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3942 p -= grid_frames / 2;
3945 return snap_frame_to_frame (p);
3948 /** Called when the selection has been cleared in any MidiRegionView.
3949 * @param rv MidiRegionView that the selection was cleared in.
3952 MidiRegionView::selection_cleared (MidiRegionView* rv)
3958 /* Clear our selection in sympathy; but don't signal the fact */
3959 clear_selection (false);
3963 MidiRegionView::note_button_release ()
3965 delete _note_player;
3970 MidiRegionView::get_channel_mode () const
3972 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3973 return rtav->midi_track()->get_playback_channel_mode();
3977 MidiRegionView::get_selected_channels () const
3979 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3980 return rtav->midi_track()->get_playback_channel_mask();
3985 MidiRegionView::get_grid_beats(framepos_t pos) const
3987 PublicEditor& editor = trackview.editor();
3988 bool success = false;
3989 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3991 beats = Evoral::MusicalTime(1);