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 midi_region()->midi_source(0)->load_model();
276 _model = midi_region()->midi_source(0)->model();
277 _enable_display = false;
278 _fill_color_name = "midi frame base";
280 RegionView::init (false);
282 set_height (trackview.current_height());
285 region_sync_changed ();
286 region_resized (ARDOUR::bounds_change);
291 _enable_display = true;
294 display_model (_model);
298 reset_width_dependent_items (_pixel_width);
300 group->raise_to_top();
302 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
303 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
306 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
307 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
309 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
310 boost::bind (&MidiRegionView::snap_changed, this),
313 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
314 connect_to_diskstream ();
316 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
320 MidiRegionView::instrument_info () const
322 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
323 return route_ui->route()->instrument_info();
326 const boost::shared_ptr<ARDOUR::MidiRegion>
327 MidiRegionView::midi_region() const
329 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
333 MidiRegionView::connect_to_diskstream ()
335 midi_view()->midi_track()->DataRecorded.connect(
336 *this, invalidator(*this),
337 boost::bind (&MidiRegionView::data_recorded, this, _1),
342 MidiRegionView::canvas_group_event(GdkEvent* ev)
344 if (in_destructor || _recregion) {
348 if (!trackview.editor().internal_editing()) {
349 // not in internal edit mode, so just act like a normal region
350 return RegionView::canvas_group_event (ev);
353 const MouseMode m = trackview.editor().current_mouse_mode();
357 case GDK_ENTER_NOTIFY:
358 _last_event_x = ev->crossing.x;
359 _last_event_y = ev->crossing.y;
360 enter_notify(&ev->crossing);
361 // set entered_regionview (among other things)
362 return RegionView::canvas_group_event (ev);
364 case GDK_LEAVE_NOTIFY:
365 _last_event_x = ev->crossing.x;
366 _last_event_y = ev->crossing.y;
367 leave_notify(&ev->crossing);
368 // reset entered_regionview (among other things)
369 return RegionView::canvas_group_event (ev);
371 case GDK_2BUTTON_PRESS:
372 // cannot use double-click to exit internal mode if single-click is being used
373 if ((m != MouseDraw) &&
375 !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier()))) {
376 return trackview.editor().toggle_internal_editing_from_double_click (ev);
381 if (scroll (&ev->scroll)) {
387 return key_press (&ev->key);
389 case GDK_KEY_RELEASE:
390 return key_release (&ev->key);
392 case GDK_BUTTON_PRESS:
393 return button_press (&ev->button);
395 case GDK_BUTTON_RELEASE:
396 r = button_release (&ev->button);
401 case GDK_MOTION_NOTIFY:
402 _last_event_x = ev->motion.x;
403 _last_event_y = ev->motion.y;
404 return motion (&ev->motion);
410 return RegionView::canvas_group_event (ev);
414 MidiRegionView::enter_notify (GdkEventCrossing* ev)
416 trackview.editor().MouseModeChanged.connect (
417 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
427 MidiRegionView::leave_notify (GdkEventCrossing*)
429 _mouse_mode_connection.disconnect ();
438 MidiRegionView::mouse_mode_changed ()
440 if (trackview.editor().internal_editing()) {
441 // Switched in to internal editing mode while entered
444 // Switched out of internal editing mode while entered
450 MidiRegionView::enter_internal()
452 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
453 // Show ghost note under pencil
454 create_ghost_note(_last_event_x, _last_event_y);
457 if (!_selection.empty()) {
458 // Grab keyboard for moving selected notes with arrow keys
459 Keyboard::magic_widget_grab_focus();
460 _grabbed_keyboard = true;
465 MidiRegionView::leave_internal()
467 trackview.editor().verbose_cursor()->hide ();
468 remove_ghost_note ();
470 if (_grabbed_keyboard) {
471 Keyboard::magic_widget_drop_focus();
472 _grabbed_keyboard = false;
477 MidiRegionView::button_press (GdkEventButton* ev)
479 if (ev->button != 1) {
483 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
484 MouseMode m = editor->current_mouse_mode();
486 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
487 pre_press_cursor = editor->get_canvas_cursor ();
488 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
491 if (_mouse_state != SelectTouchDragging) {
493 _pressed_button = ev->button;
494 _mouse_state = Pressed;
499 _pressed_button = ev->button;
505 MidiRegionView::button_release (GdkEventButton* ev)
507 double event_x, event_y;
509 if (ev->button != 1) {
516 group->canvas_to_item (event_x, event_y);
519 PublicEditor& editor = trackview.editor ();
521 if (pre_press_cursor) {
522 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
523 pre_press_cursor = 0;
526 switch (_mouse_state) {
527 case Pressed: // Clicked
529 switch (editor.current_mouse_mode()) {
531 /* no motion occured - simple click */
540 if (Keyboard::is_insert_note_event(ev)) {
542 double event_x, event_y;
546 group->canvas_to_item (event_x, event_y);
548 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
550 /* Shorten the length by 1 tick so that we can add a new note at the next
551 grid snap without it overlapping this one.
553 beats -= Evoral::MusicalTime::tick();
555 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
562 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
564 /* Shorten the length by 1 tick so that we can add a new note at the next
565 grid snap without it overlapping this one.
567 beats -= Evoral::MusicalTime::tick();
569 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
580 case SelectRectDragging:
582 editor.drags()->end_grab ((GdkEvent *) ev);
584 create_ghost_note (ev->x, ev->y);
596 MidiRegionView::motion (GdkEventMotion* ev)
598 PublicEditor& editor = trackview.editor ();
600 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
601 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
602 _mouse_state != AddDragging) {
604 create_ghost_note (ev->x, ev->y);
606 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
607 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
609 update_ghost_note (ev->x, ev->y);
611 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
613 remove_ghost_note ();
614 editor.verbose_cursor()->hide ();
616 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
618 update_ghost_note (ev->x, ev->y);
621 /* any motion immediately hides velocity text that may have been visible */
623 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
624 (*i)->hide_velocity ();
627 switch (_mouse_state) {
630 if (_pressed_button == 1) {
632 MouseMode m = editor.current_mouse_mode();
634 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
635 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
636 _mouse_state = AddDragging;
637 remove_ghost_note ();
638 editor.verbose_cursor()->hide ();
640 } else if (m == MouseObject) {
641 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
642 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
645 _mouse_state = SelectRectDragging;
647 } else if (m == MouseRange) {
648 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
649 _mouse_state = SelectVerticalDragging;
656 case SelectRectDragging:
657 case SelectVerticalDragging:
659 editor.drags()->motion_handler ((GdkEvent *) ev, false);
662 case SelectTouchDragging:
670 /* we may be dragging some non-note object (eg. patch-change, sysex)
673 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
678 MidiRegionView::scroll (GdkEventScroll* ev)
680 if (_selection.empty()) {
684 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
685 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
686 it still works for zoom.
691 trackview.editor().verbose_cursor()->hide ();
693 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
694 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
696 if (ev->direction == GDK_SCROLL_UP) {
697 change_velocities (true, fine, false, together);
698 } else if (ev->direction == GDK_SCROLL_DOWN) {
699 change_velocities (false, fine, false, together);
701 /* left, right: we don't use them */
709 MidiRegionView::key_press (GdkEventKey* ev)
711 /* since GTK bindings are generally activated on press, and since
712 detectable auto-repeat is the name of the game and only sends
713 repeated presses, carry out key actions at key press, not release.
716 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
718 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
719 _mouse_state = SelectTouchDragging;
722 } else if (ev->keyval == GDK_Escape && unmodified) {
726 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
728 bool start = (ev->keyval == GDK_comma);
729 bool end = (ev->keyval == GDK_period);
730 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
731 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
733 change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
737 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
739 if (_selection.empty()) {
746 } else if (ev->keyval == GDK_Tab) {
748 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
749 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
751 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
755 } else if (ev->keyval == GDK_ISO_Left_Tab) {
757 /* Shift-TAB generates ISO Left Tab, for some reason */
759 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
760 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
762 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
768 } else if (ev->keyval == GDK_Up) {
770 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
771 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
772 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
774 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
775 change_velocities (true, fine, allow_smush, together);
777 transpose (true, fine, allow_smush);
781 } else if (ev->keyval == GDK_Down) {
783 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
784 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
785 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
787 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
788 change_velocities (false, fine, allow_smush, together);
790 transpose (false, fine, allow_smush);
794 } else if (ev->keyval == GDK_Left) {
796 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
797 nudge_notes (false, fine);
800 } else if (ev->keyval == GDK_Right) {
802 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
803 nudge_notes (true, fine);
806 } else if (ev->keyval == GDK_c && unmodified) {
810 } else if (ev->keyval == GDK_v && unmodified) {
819 MidiRegionView::key_release (GdkEventKey* ev)
821 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
829 MidiRegionView::channel_edit ()
831 if (_selection.empty()) {
835 /* pick a note somewhat at random (since Selection is a set<>) to
836 * provide the "current" channel for the dialog.
839 uint8_t current_channel = (*_selection.begin())->note()->channel ();
840 MidiChannelDialog channel_dialog (current_channel);
841 int ret = channel_dialog.run ();
844 case Gtk::RESPONSE_OK:
850 uint8_t new_channel = channel_dialog.active_channel ();
852 start_note_diff_command (_("channel edit"));
854 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
855 Selection::iterator next = i;
857 change_note_channel (*i, new_channel);
865 MidiRegionView::velocity_edit ()
867 if (_selection.empty()) {
871 /* pick a note somewhat at random (since Selection is a set<>) to
872 * provide the "current" velocity for the dialog.
875 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
876 MidiVelocityDialog velocity_dialog (current_velocity);
877 int ret = velocity_dialog.run ();
880 case Gtk::RESPONSE_OK:
886 uint8_t new_velocity = velocity_dialog.velocity ();
888 start_note_diff_command (_("velocity edit"));
890 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
891 Selection::iterator next = i;
893 change_note_velocity (*i, new_velocity, false);
901 MidiRegionView::show_list_editor ()
904 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
906 _list_editor->present ();
909 /** Add a note to the model, and the view, at a canvas (click) coordinate.
910 * \param t time in frames relative to the position of the region
911 * \param y vertical position in pixels
912 * \param length duration of the note in beats
913 * \param snap_t true to snap t to the grid, otherwise false.
916 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
918 if (length < 2 * DBL_EPSILON) {
922 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
923 MidiStreamView* const view = mtv->midi_view();
925 const double note = view->y_to_note(y);
927 // Start of note in frames relative to region start
929 framecnt_t grid_frames;
930 t = snap_frame_to_grid_underneath (t, grid_frames);
933 const boost::shared_ptr<NoteType> new_note (
934 new NoteType (mtv->get_channel_for_add (),
935 region_frames_to_region_beats(t + _region->start()),
937 (uint8_t)note, 0x40));
939 if (_model->contains (new_note)) {
943 view->update_note_range(new_note->note());
945 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
947 _model->apply_command(*trackview.session(), cmd);
949 play_midi_note (new_note);
953 MidiRegionView::clear_events (bool with_selection_signal)
955 clear_selection (with_selection_signal);
958 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
959 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
964 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
969 _patch_changes.clear();
971 _optimization_iterator = _events.end();
975 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
979 content_connection.disconnect ();
980 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
984 if (_enable_display) {
990 MidiRegionView::start_note_diff_command (string name)
992 if (!_note_diff_command) {
993 _note_diff_command = _model->new_note_diff_command (name);
998 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1000 if (_note_diff_command) {
1001 _note_diff_command->add (note);
1004 _marked_for_selection.insert(note);
1006 if (show_velocity) {
1007 _marked_for_velocity.insert(note);
1012 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1014 if (_note_diff_command && ev->note()) {
1015 _note_diff_command->remove(ev->note());
1020 MidiRegionView::note_diff_add_change (NoteBase* ev,
1021 MidiModel::NoteDiffCommand::Property property,
1024 if (_note_diff_command) {
1025 _note_diff_command->change (ev->note(), property, val);
1030 MidiRegionView::note_diff_add_change (NoteBase* ev,
1031 MidiModel::NoteDiffCommand::Property property,
1032 Evoral::MusicalTime val)
1034 if (_note_diff_command) {
1035 _note_diff_command->change (ev->note(), property, val);
1040 MidiRegionView::apply_diff (bool as_subcommand)
1044 if (!_note_diff_command) {
1048 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1049 // Mark all selected notes for selection when model reloads
1050 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1051 _marked_for_selection.insert((*i)->note());
1055 if (as_subcommand) {
1056 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1058 _model->apply_command (*trackview.session(), _note_diff_command);
1061 _note_diff_command = 0;
1062 midi_view()->midi_track()->playlist_modified();
1064 if (add_or_remove) {
1065 _marked_for_selection.clear();
1068 _marked_for_velocity.clear();
1072 MidiRegionView::abort_command()
1074 delete _note_diff_command;
1075 _note_diff_command = 0;
1080 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1082 if (_optimization_iterator != _events.end()) {
1083 ++_optimization_iterator;
1086 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1087 return *_optimization_iterator;
1090 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1091 if ((*_optimization_iterator)->note() == note) {
1092 return *_optimization_iterator;
1100 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1102 MidiModel::Notes notes;
1103 _model->get_notes (notes, op, val, chan_mask);
1105 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1106 NoteBase* cne = find_canvas_note (*n);
1114 MidiRegionView::redisplay_model()
1116 // Don't redisplay the model if we're currently recording and displaying that
1117 if (_active_notes) {
1125 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1126 (*i)->invalidate ();
1129 MidiModel::ReadLock lock(_model->read_lock());
1131 MidiModel::Notes& notes (_model->notes());
1132 _optimization_iterator = _events.begin();
1134 bool empty_when_starting = _events.empty();
1136 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1138 boost::shared_ptr<NoteType> note (*n);
1142 if (note_in_region_range (note, visible)) {
1144 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1151 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1153 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1165 add_note (note, visible);
1170 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1178 /* remove note items that are no longer valid */
1180 if (!empty_when_starting) {
1181 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1182 if (!(*i)->valid ()) {
1184 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1185 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1187 gr->remove_note (*i);
1192 i = _events.erase (i);
1200 _patch_changes.clear();
1204 display_patch_changes ();
1206 _marked_for_selection.clear ();
1207 _marked_for_velocity.clear ();
1209 /* we may have caused _events to contain things out of order (e.g. if a note
1210 moved earlier or later). we don't generally need them in time order, but
1211 make a note that a sort is required for those cases that require it.
1214 _sort_needed = true;
1218 MidiRegionView::display_patch_changes ()
1220 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1221 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1223 for (uint8_t i = 0; i < 16; ++i) {
1224 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1228 /** @param active_channel true to display patch changes fully, false to display
1229 * them `greyed-out' (as on an inactive channel)
1232 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1234 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1236 if ((*i)->channel() != channel) {
1240 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1241 add_canvas_patch_change (*i, patch_name, active_channel);
1246 MidiRegionView::display_sysexes()
1248 bool have_periodic_system_messages = false;
1249 bool display_periodic_messages = true;
1251 if (!Config->get_never_display_periodic_midi()) {
1253 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1254 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1255 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1258 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1259 have_periodic_system_messages = true;
1265 if (have_periodic_system_messages) {
1266 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1268 /* get an approximate value for the number of samples per video frame */
1270 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1272 /* if we are zoomed out beyond than the cutoff (i.e. more
1273 * frames per pixel than frames per 4 video frames), don't
1274 * show periodic sysex messages.
1277 if (zoom > (video_frame*4)) {
1278 display_periodic_messages = false;
1282 display_periodic_messages = false;
1285 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1287 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1288 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1290 Evoral::MusicalTime time = (*i)->time();
1293 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1294 if (!display_periodic_messages) {
1302 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1303 str << int((*i)->buffer()[b]);
1304 if (b != (*i)->size() -1) {
1308 string text = str.str();
1310 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1312 double height = midi_stream_view()->contents_height();
1314 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1315 // SysEx canvas object!!!
1317 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1318 new SysEx (*this, _note_group, text, height, x, 1.0));
1320 // Show unless message is beyond the region bounds
1321 if (time - _region->start() >= _region->length() || time < _region->start()) {
1327 _sys_exes.push_back(sysex);
1331 MidiRegionView::~MidiRegionView ()
1333 in_destructor = true;
1335 trackview.editor().verbose_cursor()->hide ();
1337 note_delete_connection.disconnect ();
1339 delete _list_editor;
1341 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1343 if (_active_notes) {
1347 _selection_cleared_connection.disconnect ();
1350 clear_events (false);
1353 delete _note_diff_command;
1354 delete _step_edit_cursor;
1355 delete _temporary_note_group;
1359 MidiRegionView::region_resized (const PropertyChange& what_changed)
1361 RegionView::region_resized(what_changed);
1363 if (what_changed.contains (ARDOUR::Properties::position)) {
1364 _region_relative_time_converter.set_origin_b(_region->position());
1365 set_duration(_region->length(), 0);
1366 if (_enable_display) {
1371 if (what_changed.contains (ARDOUR::Properties::start) ||
1372 what_changed.contains (ARDOUR::Properties::position)) {
1373 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1378 MidiRegionView::reset_width_dependent_items (double pixel_width)
1380 RegionView::reset_width_dependent_items(pixel_width);
1382 if (_enable_display) {
1386 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1387 if ((*x)->canvas_item()->width() >= _pixel_width) {
1394 move_step_edit_cursor (_step_edit_cursor_position);
1395 set_step_edit_cursor_width (_step_edit_cursor_width);
1399 MidiRegionView::set_height (double height)
1401 double old_height = _height;
1402 RegionView::set_height(height);
1404 apply_note_range (midi_stream_view()->lowest_note(),
1405 midi_stream_view()->highest_note(),
1406 height != old_height);
1409 name_text->raise_to_top();
1412 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1413 (*x)->set_height (midi_stream_view()->contents_height());
1416 if (_step_edit_cursor) {
1417 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1422 /** Apply the current note range from the stream view
1423 * by repositioning/hiding notes as necessary
1426 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1428 if (!_enable_display) {
1432 if (!force && _current_range_min == min && _current_range_max == max) {
1436 _current_range_min = min;
1437 _current_range_max = max;
1439 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1440 NoteBase* event = *i;
1441 boost::shared_ptr<NoteType> note (event->note());
1443 if (note->note() < _current_range_min ||
1444 note->note() > _current_range_max) {
1450 if (Note* cnote = dynamic_cast<Note*>(event)) {
1452 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1453 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1458 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1465 MidiRegionView::add_ghost (TimeAxisView& tv)
1469 double unit_position = _region->position () / samples_per_pixel;
1470 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1471 MidiGhostRegion* ghost;
1473 if (mtv && mtv->midi_view()) {
1474 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1475 to allow having midi notes on top of note lines and waveforms.
1477 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1479 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1482 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1483 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1484 ghost->add_note(note);
1488 ghost->set_height ();
1489 ghost->set_duration (_region->length() / samples_per_pixel);
1490 ghosts.push_back (ghost);
1492 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1498 /** Begin tracking note state for successive calls to add_event
1501 MidiRegionView::begin_write()
1503 if (_active_notes) {
1504 delete[] _active_notes;
1506 _active_notes = new Note*[128];
1507 for (unsigned i = 0; i < 128; ++i) {
1508 _active_notes[i] = 0;
1513 /** Destroy note state for add_event
1516 MidiRegionView::end_write()
1518 delete[] _active_notes;
1520 _marked_for_selection.clear();
1521 _marked_for_velocity.clear();
1525 /** Resolve an active MIDI note (while recording).
1528 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1530 if (midi_view()->note_mode() != Sustained) {
1534 if (_active_notes && _active_notes[note]) {
1536 /* XXX is end_time really region-centric? I think so, because
1537 this is a new region that we're recording, so source zero is
1538 the same as region zero
1540 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1542 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1543 _active_notes[note]->set_outline_all ();
1544 _active_notes[note] = 0;
1550 /** Extend active notes to rightmost edge of region (if length is changed)
1553 MidiRegionView::extend_active_notes()
1555 if (!_active_notes) {
1559 for (unsigned i=0; i < 128; ++i) {
1560 if (_active_notes[i]) {
1561 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1568 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1570 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1574 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1576 if (!route_ui || !route_ui->midi_track()) {
1580 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1584 /* NotePlayer deletes itself */
1588 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1590 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1594 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1596 if (!route_ui || !route_ui->midi_track()) {
1600 delete _note_player;
1601 _note_player = new NotePlayer (route_ui->midi_track ());
1602 _note_player->add (note);
1603 _note_player->on ();
1607 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1609 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1613 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1615 if (!route_ui || !route_ui->midi_track()) {
1619 delete _note_player;
1620 _note_player = new NotePlayer (route_ui->midi_track());
1622 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1623 _note_player->add (*n);
1626 _note_player->on ();
1631 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1633 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1634 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1636 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1637 (note->note() <= midi_stream_view()->highest_note());
1642 /** Update a canvas note's size from its model note.
1643 * @param ev Canvas note to update.
1644 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1647 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1649 boost::shared_ptr<NoteType> note = ev->note();
1650 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1651 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1656 /* trim note display to not overlap the end of its region */
1658 if (note->length() > 0) {
1659 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1660 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1662 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1665 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1667 if (!note->length()) {
1668 if (_active_notes && note->note() < 128) {
1669 // If this note is already active there's a stuck note,
1670 // finish the old note rectangle
1671 if (_active_notes[note->note()]) {
1672 Note* const old_rect = _active_notes[note->note()];
1673 boost::shared_ptr<NoteType> old_note = old_rect->note();
1674 old_rect->set_x1 (x);
1675 old_rect->set_outline_all ();
1677 _active_notes[note->note()] = ev;
1679 /* outline all but right edge */
1680 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1681 ArdourCanvas::Rectangle::TOP|
1682 ArdourCanvas::Rectangle::LEFT|
1683 ArdourCanvas::Rectangle::BOTTOM));
1685 /* outline all edges */
1686 ev->set_outline_all ();
1689 if (update_ghost_regions) {
1690 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1691 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1693 gr->update_note (ev);
1700 MidiRegionView::update_hit (Hit* ev)
1702 boost::shared_ptr<NoteType> note = ev->note();
1704 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1705 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1706 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1707 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1709 ev->set_position (ArdourCanvas::Duple (x, y));
1710 ev->set_height (diamond_size);
1713 /** Add a MIDI note to the view (with length).
1715 * If in sustained mode, notes with length 0 will be considered active
1716 * notes, and resolve_note should be called when the corresponding note off
1717 * event arrives, to properly display the note.
1720 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1722 NoteBase* event = 0;
1724 if (midi_view()->note_mode() == Sustained) {
1726 Note* ev_rect = new Note (*this, _note_group, note);
1728 update_note (ev_rect);
1732 MidiGhostRegion* gr;
1734 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1735 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1736 gr->add_note(ev_rect);
1740 } else if (midi_view()->note_mode() == Percussive) {
1742 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1744 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1746 update_hit (ev_diamond);
1755 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1756 note_selected(event, true);
1759 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1760 event->show_velocity();
1763 event->on_channel_selection_change (get_selected_channels());
1764 _events.push_back(event);
1773 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1774 MidiStreamView* const view = mtv->midi_view();
1776 view->update_note_range (note->note());
1780 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1781 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1783 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1785 /* potentially extend region to hold new note */
1787 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1788 framepos_t region_end = _region->last_frame();
1790 if (end_frame > region_end) {
1791 _region->set_length (end_frame - _region->position());
1794 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1795 MidiStreamView* const view = mtv->midi_view();
1797 view->update_note_range(new_note->note());
1799 _marked_for_selection.clear ();
1802 start_note_diff_command (_("step add"));
1803 note_diff_add_note (new_note, true, false);
1806 // last_step_edit_note = new_note;
1810 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1812 change_note_lengths (false, false, beats, false, true);
1815 /** Add a new patch change flag to the canvas.
1816 * @param patch the patch change to add
1817 * @param the text to display in the flag
1818 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1821 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1823 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1824 const double x = trackview.editor().sample_to_pixel (region_frames);
1826 double const height = midi_stream_view()->contents_height();
1828 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1829 // so we need to do something more sophisticated to keep its color
1830 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1833 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1834 new PatchChange(*this, group,
1841 if (patch_change->item().width() < _pixel_width) {
1842 // Show unless patch change is beyond the region bounds
1843 if (region_frames < 0 || region_frames >= _region->length()) {
1844 patch_change->hide();
1846 patch_change->show();
1849 patch_change->hide ();
1852 _patch_changes.push_back (patch_change);
1855 MIDI::Name::PatchPrimaryKey
1856 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1858 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1861 /// Return true iff @p pc applies to the given time on the given channel.
1863 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1865 return pc->time() <= time && pc->channel() == channel;
1869 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1871 // The earliest event not before time
1872 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1874 // Go backwards until we find the latest PC for this channel, or the start
1875 while (i != _model->patch_changes().begin() &&
1876 (i == _model->patch_changes().end() ||
1877 !patch_applies(*i, time, channel))) {
1881 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1882 key.bank_number = (*i)->bank();
1883 key.program_number = (*i)->program ();
1885 key.bank_number = key.program_number = 0;
1888 if (!key.is_sane()) {
1889 error << string_compose(_("insane MIDI patch key %1:%2"),
1890 key.bank_number, key.program_number) << endmsg;
1895 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1897 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1899 if (pc.patch()->program() != new_patch.program_number) {
1900 c->change_program (pc.patch (), new_patch.program_number);
1903 int const new_bank = new_patch.bank_number;
1904 if (pc.patch()->bank() != new_bank) {
1905 c->change_bank (pc.patch (), new_bank);
1908 _model->apply_command (*trackview.session(), c);
1910 _patch_changes.clear ();
1911 display_patch_changes ();
1915 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1917 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1919 if (old_change->time() != new_change.time()) {
1920 c->change_time (old_change, new_change.time());
1923 if (old_change->channel() != new_change.channel()) {
1924 c->change_channel (old_change, new_change.channel());
1927 if (old_change->program() != new_change.program()) {
1928 c->change_program (old_change, new_change.program());
1931 if (old_change->bank() != new_change.bank()) {
1932 c->change_bank (old_change, new_change.bank());
1935 _model->apply_command (*trackview.session(), c);
1937 _patch_changes.clear ();
1938 display_patch_changes ();
1941 /** Add a patch change to the region.
1942 * @param t Time in frames relative to region position
1943 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1944 * MidiTimeAxisView::get_channel_for_add())
1947 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1949 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1951 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1952 c->add (MidiModel::PatchChangePtr (
1953 new Evoral::PatchChange<Evoral::MusicalTime> (
1954 absolute_frames_to_source_beats (_region->position() + t),
1955 mtv->get_channel_for_add(), patch.program(), patch.bank()
1960 _model->apply_command (*trackview.session(), c);
1962 _patch_changes.clear ();
1963 display_patch_changes ();
1967 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1969 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1970 c->change_time (pc.patch (), t);
1971 _model->apply_command (*trackview.session(), c);
1973 _patch_changes.clear ();
1974 display_patch_changes ();
1978 MidiRegionView::delete_patch_change (PatchChange* pc)
1980 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1981 c->remove (pc->patch ());
1982 _model->apply_command (*trackview.session(), c);
1984 _patch_changes.clear ();
1985 display_patch_changes ();
1989 MidiRegionView::previous_patch (PatchChange& patch)
1991 if (patch.patch()->program() < 127) {
1992 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1993 key.program_number++;
1994 change_patch_change (patch, key);
1999 MidiRegionView::next_patch (PatchChange& patch)
2001 if (patch.patch()->program() > 0) {
2002 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2003 key.program_number--;
2004 change_patch_change (patch, key);
2009 MidiRegionView::next_bank (PatchChange& patch)
2011 if (patch.patch()->program() < 127) {
2012 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2013 if (key.bank_number > 0) {
2015 change_patch_change (patch, key);
2021 MidiRegionView::previous_bank (PatchChange& patch)
2023 if (patch.patch()->program() > 0) {
2024 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2025 if (key.bank_number < 127) {
2027 change_patch_change (patch, key);
2033 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2035 if (_selection.empty()) {
2039 _selection.erase (cne);
2043 MidiRegionView::delete_selection()
2045 if (_selection.empty()) {
2049 start_note_diff_command (_("delete selection"));
2051 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2052 if ((*i)->selected()) {
2053 _note_diff_command->remove((*i)->note());
2063 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2065 start_note_diff_command (_("delete note"));
2066 _note_diff_command->remove (n);
2069 trackview.editor().verbose_cursor()->hide ();
2073 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2075 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2077 Selection::iterator tmp = i;
2080 (*i)->set_selected (false);
2081 (*i)->hide_velocity ();
2082 _selection.erase (i);
2090 if (!ev && _entered) {
2091 // Clearing selection entirely, ungrab keyboard
2092 Keyboard::magic_widget_drop_focus();
2093 _grabbed_keyboard = false;
2096 /* this does not change the status of this regionview w.r.t the editor
2101 SelectionCleared (this); /* EMIT SIGNAL */
2106 MidiRegionView::unique_select(NoteBase* ev)
2108 const bool selection_was_empty = _selection.empty();
2110 clear_selection_except (ev);
2112 /* don't bother with checking to see if we should remove this
2113 regionview from the editor selection, since we're about to add
2114 another note, and thus put/keep this regionview in the editor
2118 if (!ev->selected()) {
2119 add_to_selection (ev);
2120 if (selection_was_empty && _entered) {
2121 // Grab keyboard for moving notes with arrow keys
2122 Keyboard::magic_widget_grab_focus();
2123 _grabbed_keyboard = true;
2129 MidiRegionView::select_all_notes ()
2133 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2134 add_to_selection (*i);
2139 MidiRegionView::select_range (framepos_t start, framepos_t end)
2143 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2144 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2145 if (t >= start && t <= end) {
2146 add_to_selection (*i);
2152 MidiRegionView::invert_selection ()
2154 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2155 if ((*i)->selected()) {
2156 remove_from_selection(*i);
2158 add_to_selection (*i);
2164 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2166 bool have_selection = !_selection.empty();
2167 uint8_t low_note = 127;
2168 uint8_t high_note = 0;
2169 MidiModel::Notes& notes (_model->notes());
2170 _optimization_iterator = _events.begin();
2172 if (extend && !have_selection) {
2176 /* scan existing selection to get note range */
2178 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2179 if ((*i)->note()->note() < low_note) {
2180 low_note = (*i)->note()->note();
2182 if ((*i)->note()->note() > high_note) {
2183 high_note = (*i)->note()->note();
2190 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2191 /* only note previously selected is the one we are
2192 * reselecting. treat this as cancelling the selection.
2199 low_note = min (low_note, notenum);
2200 high_note = max (high_note, notenum);
2203 _no_sound_notes = true;
2205 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2207 boost::shared_ptr<NoteType> note (*n);
2209 bool select = false;
2211 if (((1 << note->channel()) & channel_mask) != 0) {
2213 if ((note->note() >= low_note && note->note() <= high_note)) {
2216 } else if (note->note() == notenum) {
2222 if ((cne = find_canvas_note (note)) != 0) {
2223 // extend is false because we've taken care of it,
2224 // since it extends by time range, not pitch.
2225 note_selected (cne, add, false);
2229 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2233 _no_sound_notes = false;
2237 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2239 MidiModel::Notes& notes (_model->notes());
2240 _optimization_iterator = _events.begin();
2242 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2244 boost::shared_ptr<NoteType> note (*n);
2247 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2248 if ((cne = find_canvas_note (note)) != 0) {
2249 if (cne->selected()) {
2250 note_deselected (cne);
2252 note_selected (cne, true, false);
2260 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2263 clear_selection_except (ev);
2264 if (!_selection.empty()) {
2265 PublicEditor& editor (trackview.editor());
2266 editor.get_selection().add (this);
2272 if (!ev->selected()) {
2273 add_to_selection (ev);
2277 /* find end of latest note selected, select all between that and the start of "ev" */
2279 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2280 Evoral::MusicalTime latest = Evoral::MusicalTime();
2282 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2283 if ((*i)->note()->end_time() > latest) {
2284 latest = (*i)->note()->end_time();
2286 if ((*i)->note()->time() < earliest) {
2287 earliest = (*i)->note()->time();
2291 if (ev->note()->end_time() > latest) {
2292 latest = ev->note()->end_time();
2295 if (ev->note()->time() < earliest) {
2296 earliest = ev->note()->time();
2299 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2301 /* find notes entirely within OR spanning the earliest..latest range */
2303 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2304 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2305 add_to_selection (*i);
2313 MidiRegionView::note_deselected(NoteBase* ev)
2315 remove_from_selection (ev);
2319 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2321 PublicEditor& editor = trackview.editor();
2323 // Convert to local coordinates
2324 const framepos_t p = _region->position();
2325 const double y = midi_view()->y_position();
2326 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2327 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2328 const double y0 = max(0.0, gy0 - y);
2329 const double y1 = max(0.0, gy1 - y);
2331 // TODO: Make this faster by storing the last updated selection rect, and only
2332 // adjusting things that are in the area that appears/disappeared.
2333 // We probably need a tree to be able to find events in O(log(n)) time.
2335 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2336 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2337 // Rectangles intersect
2338 if (!(*i)->selected()) {
2339 add_to_selection (*i);
2341 } else if ((*i)->selected() && !extend) {
2342 // Rectangles do not intersect
2343 remove_from_selection (*i);
2347 typedef RouteTimeAxisView::AutomationTracks ATracks;
2348 typedef std::list<Selectable*> Selectables;
2350 /* Add control points to selection. */
2351 const ATracks& atracks = midi_view()->automation_tracks();
2352 Selectables selectables;
2353 editor.get_selection().clear_points();
2354 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2355 a->second->get_selectables(start, end, gy0, gy1, selectables);
2356 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2357 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2359 editor.get_selection().add(cp);
2362 a->second->set_selected_points(editor.get_selection().points);
2367 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2373 // TODO: Make this faster by storing the last updated selection rect, and only
2374 // adjusting things that are in the area that appears/disappeared.
2375 // We probably need a tree to be able to find events in O(log(n)) time.
2377 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2378 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2379 // within y- (note-) range
2380 if (!(*i)->selected()) {
2381 add_to_selection (*i);
2383 } else if ((*i)->selected() && !extend) {
2384 remove_from_selection (*i);
2390 MidiRegionView::remove_from_selection (NoteBase* ev)
2392 Selection::iterator i = _selection.find (ev);
2394 if (i != _selection.end()) {
2395 _selection.erase (i);
2396 if (_selection.empty() && _grabbed_keyboard) {
2398 Keyboard::magic_widget_drop_focus();
2399 _grabbed_keyboard = false;
2403 ev->set_selected (false);
2404 ev->hide_velocity ();
2406 if (_selection.empty()) {
2407 PublicEditor& editor (trackview.editor());
2408 editor.get_selection().remove (this);
2413 MidiRegionView::add_to_selection (NoteBase* ev)
2415 const bool selection_was_empty = _selection.empty();
2417 if (_selection.insert (ev).second) {
2418 ev->set_selected (true);
2419 start_playing_midi_note ((ev)->note());
2420 if (selection_was_empty && _entered) {
2421 // Grab keyboard for moving notes with arrow keys
2422 Keyboard::magic_widget_grab_focus();
2423 _grabbed_keyboard = true;
2427 if (selection_was_empty) {
2428 PublicEditor& editor (trackview.editor());
2429 editor.get_selection().add (this);
2434 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2436 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2437 PossibleChord to_play;
2438 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2440 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2441 if ((*i)->note()->time() < earliest) {
2442 earliest = (*i)->note()->time();
2446 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2447 if ((*i)->note()->time() == earliest) {
2448 to_play.push_back ((*i)->note());
2450 (*i)->move_event(dx, dy);
2453 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2455 if (to_play.size() > 1) {
2457 PossibleChord shifted;
2459 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2460 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2461 moved_note->set_note (moved_note->note() + cumulative_dy);
2462 shifted.push_back (moved_note);
2465 start_playing_midi_chord (shifted);
2467 } else if (!to_play.empty()) {
2469 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2470 moved_note->set_note (moved_note->note() + cumulative_dy);
2471 start_playing_midi_note (moved_note);
2477 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2479 uint8_t lowest_note_in_selection = 127;
2480 uint8_t highest_note_in_selection = 0;
2481 uint8_t highest_note_difference = 0;
2483 // find highest and lowest notes first
2485 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2486 uint8_t pitch = (*i)->note()->note();
2487 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2488 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2492 cerr << "dnote: " << (int) dnote << endl;
2493 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2494 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2495 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2496 << int(highest_note_in_selection) << endl;
2497 cerr << "selection size: " << _selection.size() << endl;
2498 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2501 // Make sure the note pitch does not exceed the MIDI standard range
2502 if (highest_note_in_selection + dnote > 127) {
2503 highest_note_difference = highest_note_in_selection - 127;
2506 start_note_diff_command (_("move notes"));
2508 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2510 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2511 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2517 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2519 uint8_t original_pitch = (*i)->note()->note();
2520 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2522 // keep notes in standard midi range
2523 clamp_to_0_127(new_pitch);
2525 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2526 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2528 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2533 // care about notes being moved beyond the upper/lower bounds on the canvas
2534 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2535 highest_note_in_selection > midi_stream_view()->highest_note()) {
2536 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2540 /** @param x Pixel relative to the region position.
2541 * @return Snapped frame relative to the region position.
2544 MidiRegionView::snap_pixel_to_sample(double x)
2546 PublicEditor& editor (trackview.editor());
2547 return snap_frame_to_frame (editor.pixel_to_sample (x));
2550 /** @param x Pixel relative to the region position.
2551 * @return Snapped pixel relative to the region position.
2554 MidiRegionView::snap_to_pixel(double x)
2556 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2560 MidiRegionView::get_position_pixels()
2562 framepos_t region_frame = get_position();
2563 return trackview.editor().sample_to_pixel(region_frame);
2567 MidiRegionView::get_end_position_pixels()
2569 framepos_t frame = get_position() + get_duration ();
2570 return trackview.editor().sample_to_pixel(frame);
2574 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2576 /* the time converter will return the frame corresponding to `beats'
2577 relative to the start of the source. The start of the source
2578 is an implied position given by region->position - region->start
2580 const framepos_t source_start = _region->position() - _region->start();
2581 return source_start + _source_relative_time_converter.to (beats);
2585 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2587 /* the `frames' argument needs to be converted into a frame count
2588 relative to the start of the source before being passed in to the
2591 const framepos_t source_start = _region->position() - _region->start();
2592 return _source_relative_time_converter.from (frames - source_start);
2596 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2598 return _region_relative_time_converter.to(beats);
2602 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2604 return _region_relative_time_converter.from(frames);
2608 MidiRegionView::begin_resizing (bool /*at_front*/)
2610 _resize_data.clear();
2612 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2613 Note *note = dynamic_cast<Note*> (*i);
2615 // only insert CanvasNotes into the map
2617 NoteResizeData *resize_data = new NoteResizeData();
2618 resize_data->note = note;
2620 // create a new SimpleRect from the note which will be the resize preview
2621 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2622 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2624 // calculate the colors: get the color settings
2625 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2626 ARDOUR_UI::config()->color ("midi note selected"),
2629 // make the resize preview notes more transparent and bright
2630 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2632 // calculate color based on note velocity
2633 resize_rect->set_fill_color (UINT_INTERPOLATE(
2634 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2638 resize_rect->set_outline_color (NoteBase::calculate_outline (
2639 ARDOUR_UI::config()->color ("midi note selected")));
2641 resize_data->resize_rect = resize_rect;
2642 _resize_data.push_back(resize_data);
2647 /** Update resizing notes while user drags.
2648 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2649 * @param at_front which end of the note (true == note on, false == note off)
2650 * @param delta_x change in mouse position since the start of the drag
2651 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2652 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2653 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2654 * as the \a primary note.
2657 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2659 bool cursor_set = false;
2661 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2662 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2663 Note* canvas_note = (*i)->note;
2668 current_x = canvas_note->x0() + delta_x;
2670 current_x = primary->x0() + delta_x;
2674 current_x = canvas_note->x1() + delta_x;
2676 current_x = primary->x1() + delta_x;
2680 if (current_x < 0) {
2681 // This works even with snapping because RegionView::snap_frame_to_frame()
2682 // snaps forward if the snapped sample is before the beginning of the region
2685 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2686 current_x = trackview.editor().sample_to_pixel(_region->length());
2690 resize_rect->set_x0 (snap_to_pixel(current_x));
2691 resize_rect->set_x1 (canvas_note->x1());
2693 resize_rect->set_x1 (snap_to_pixel(current_x));
2694 resize_rect->set_x0 (canvas_note->x0());
2698 const double snapped_x = snap_pixel_to_sample (current_x);
2699 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2700 Evoral::MusicalTime len = Evoral::MusicalTime();
2703 if (beats < canvas_note->note()->end_time()) {
2704 len = canvas_note->note()->time() - beats;
2705 len += canvas_note->note()->length();
2708 if (beats >= canvas_note->note()->time()) {
2709 len = beats - canvas_note->note()->time();
2714 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2715 show_verbose_cursor (buf, 0, 0);
2724 /** Finish resizing notes when the user releases the mouse button.
2725 * Parameters the same as for \a update_resizing().
2728 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2730 start_note_diff_command (_("resize notes"));
2732 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2733 Note* canvas_note = (*i)->note;
2734 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2736 /* Get the new x position for this resize, which is in pixels relative
2737 * to the region position.
2744 current_x = canvas_note->x0() + delta_x;
2746 current_x = primary->x0() + delta_x;
2750 current_x = canvas_note->x1() + delta_x;
2752 current_x = primary->x1() + delta_x;
2756 if (current_x < 0) {
2759 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2760 current_x = trackview.editor().sample_to_pixel(_region->length());
2763 /* Convert that to a frame within the source */
2764 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2766 /* and then to beats */
2767 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2769 if (at_front && x_beats < canvas_note->note()->end_time()) {
2770 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2772 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2773 len += canvas_note->note()->length();
2776 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2781 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2784 /* XXX convert to beats */
2785 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2793 _resize_data.clear();
2798 MidiRegionView::abort_resizing ()
2800 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2801 delete (*i)->resize_rect;
2805 _resize_data.clear ();
2809 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2811 uint8_t new_velocity;
2814 new_velocity = event->note()->velocity() + velocity;
2815 clamp_to_0_127(new_velocity);
2817 new_velocity = velocity;
2820 event->set_selected (event->selected()); // change color
2822 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2826 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2831 new_note = event->note()->note() + note;
2836 clamp_to_0_127 (new_note);
2837 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2841 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2843 bool change_start = false;
2844 bool change_length = false;
2845 Evoral::MusicalTime new_start;
2846 Evoral::MusicalTime new_length;
2848 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2850 front_delta: if positive - move the start of the note later in time (shortening it)
2851 if negative - move the start of the note earlier in time (lengthening it)
2853 end_delta: if positive - move the end of the note later in time (lengthening it)
2854 if negative - move the end of the note earlier in time (shortening it)
2857 if (!!front_delta) {
2858 if (front_delta < 0) {
2860 if (event->note()->time() < -front_delta) {
2861 new_start = Evoral::MusicalTime();
2863 new_start = event->note()->time() + front_delta; // moves earlier
2866 /* start moved toward zero, so move the end point out to where it used to be.
2867 Note that front_delta is negative, so this increases the length.
2870 new_length = event->note()->length() - front_delta;
2871 change_start = true;
2872 change_length = true;
2876 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2878 if (new_pos < event->note()->end_time()) {
2879 new_start = event->note()->time() + front_delta;
2880 /* start moved toward the end, so move the end point back to where it used to be */
2881 new_length = event->note()->length() - front_delta;
2882 change_start = true;
2883 change_length = true;
2890 bool can_change = true;
2891 if (end_delta < 0) {
2892 if (event->note()->length() < -end_delta) {
2898 new_length = event->note()->length() + end_delta;
2899 change_length = true;
2904 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2907 if (change_length) {
2908 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2913 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2915 uint8_t new_channel;
2919 if (event->note()->channel() < -chn) {
2922 new_channel = event->note()->channel() + chn;
2925 new_channel = event->note()->channel() + chn;
2928 new_channel = (uint8_t) chn;
2931 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2935 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2937 Evoral::MusicalTime new_time;
2941 if (event->note()->time() < -delta) {
2942 new_time = Evoral::MusicalTime();
2944 new_time = event->note()->time() + delta;
2947 new_time = event->note()->time() + delta;
2953 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2957 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2959 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2963 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2968 if (_selection.empty()) {
2983 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2984 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2990 start_note_diff_command (_("change velocities"));
2992 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2993 Selection::iterator next = i;
2997 if (i == _selection.begin()) {
2998 change_note_velocity (*i, delta, true);
2999 value = (*i)->note()->velocity() + delta;
3001 change_note_velocity (*i, value, false);
3005 change_note_velocity (*i, delta, true);
3014 if (!_selection.empty()) {
3016 snprintf (buf, sizeof (buf), "Vel %d",
3017 (int) (*_selection.begin())->note()->velocity());
3018 show_verbose_cursor (buf, 10, 10);
3024 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3026 if (_selection.empty()) {
3043 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3045 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3049 if ((int8_t) (*i)->note()->note() + delta > 127) {
3056 start_note_diff_command (_("transpose"));
3058 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3059 Selection::iterator next = i;
3061 change_note_note (*i, delta, true);
3069 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3073 delta = Evoral::MusicalTime(1.0/128.0);
3075 /* grab the current grid distance */
3076 delta = get_grid_beats(_region->position());
3084 start_note_diff_command (_("change note lengths"));
3086 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3087 Selection::iterator next = i;
3090 /* note the negation of the delta for start */
3093 (start ? -delta : Evoral::MusicalTime()),
3094 (end ? delta : Evoral::MusicalTime()));
3103 MidiRegionView::nudge_notes (bool forward, bool fine)
3105 if (_selection.empty()) {
3109 /* pick a note as the point along the timeline to get the nudge distance.
3110 its not necessarily the earliest note, so we may want to pull the notes out
3111 into a vector and sort before using the first one.
3114 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3115 Evoral::MusicalTime delta;
3119 /* non-fine, move by 1 bar regardless of snap */
3120 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3122 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3124 /* grid is off - use nudge distance */
3127 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3128 delta = region_frames_to_region_beats (fabs ((double)distance));
3134 framepos_t next_pos = ref_point;
3137 if (max_framepos - 1 < next_pos) {
3141 if (next_pos == 0) {
3147 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3148 const framecnt_t distance = ref_point - next_pos;
3149 delta = region_frames_to_region_beats (fabs ((double)distance));
3160 start_note_diff_command (_("nudge"));
3162 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3163 Selection::iterator next = i;
3165 change_note_time (*i, delta, true);
3173 MidiRegionView::change_channel(uint8_t channel)
3175 start_note_diff_command(_("change channel"));
3176 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3177 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3185 MidiRegionView::note_entered(NoteBase* ev)
3187 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3189 pre_note_enter_cursor = editor->get_canvas_cursor ();
3191 if (_mouse_state == SelectTouchDragging) {
3192 note_selected (ev, true);
3195 show_verbose_cursor (ev->note ());
3199 MidiRegionView::note_left (NoteBase*)
3201 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3203 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3204 (*i)->hide_velocity ();
3207 editor->verbose_cursor()->hide ();
3209 if (pre_note_enter_cursor) {
3210 editor->set_canvas_cursor (pre_note_enter_cursor);
3211 pre_note_enter_cursor = 0;
3216 MidiRegionView::patch_entered (PatchChange* p)
3219 /* XXX should get patch name if we can */
3220 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3221 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3222 << _("Channel ") << ((int) p->patch()->channel() + 1);
3223 show_verbose_cursor (s.str(), 10, 20);
3224 p->item().grab_focus();
3228 MidiRegionView::patch_left (PatchChange *)
3230 trackview.editor().verbose_cursor()->hide ();
3231 /* focus will transfer back via the enter-notify event sent to this
3237 MidiRegionView::sysex_entered (SysEx* p)
3241 // need a way to extract text from p->_flag->_text
3243 // show_verbose_cursor (s.str(), 10, 20);
3244 p->item().grab_focus();
3248 MidiRegionView::sysex_left (SysEx *)
3250 trackview.editor().verbose_cursor()->hide ();
3251 /* focus will transfer back via the enter-notify event sent to this
3257 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3259 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3260 Editing::MouseMode mm = editor->current_mouse_mode();
3261 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3263 if (can_set_cursor) {
3264 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3265 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3266 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3267 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3268 } else if (pre_note_enter_cursor) {
3269 editor->set_canvas_cursor (pre_note_enter_cursor);
3275 MidiRegionView::set_frame_color()
3279 TimeAxisViewItem::set_frame_color ();
3286 f = ARDOUR_UI::config()->color ("selected region base");
3287 } else if (high_enough_for_name || !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3288 f = ARDOUR_UI::config()->color_mod ("midi frame base", "midi frame base");
3293 if (!rect_visible) {
3294 f = UINT_RGBA_CHANGE_A (f, 80);
3297 frame->set_fill_color (f);
3301 MidiRegionView::midi_channel_mode_changed ()
3303 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3304 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3305 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3307 if (mode == ForceChannel) {
3308 mask = 0xFFFF; // Show all notes as active (below)
3311 // Update notes for selection
3312 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3313 (*i)->on_channel_selection_change (mask);
3316 _patch_changes.clear ();
3317 display_patch_changes ();
3321 MidiRegionView::instrument_settings_changed ()
3327 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3329 if (_selection.empty()) {
3333 PublicEditor& editor (trackview.editor());
3337 /* XXX what to do ? */
3341 editor.get_cut_buffer().add (selection_as_cut_buffer());
3349 start_note_diff_command();
3351 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3358 note_diff_remove_note (*i);
3368 MidiRegionView::selection_as_cut_buffer () const
3372 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3373 NoteType* n = (*i)->note().get();
3374 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3377 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3383 /** This method handles undo */
3385 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3387 trackview.session()->begin_reversible_command (Operations::paste);
3389 // Paste notes, if available
3390 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3391 if (m != selection.midi_notes.end()) {
3392 ctx.counts.increase_n_notes();
3393 paste_internal(pos, ctx.count, ctx.times, **m);
3396 // Paste control points to automation children, if available
3397 typedef RouteTimeAxisView::AutomationTracks ATracks;
3398 const ATracks& atracks = midi_view()->automation_tracks();
3399 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3400 a->second->paste(pos, selection, ctx);
3403 trackview.session()->commit_reversible_command ();
3408 /** This method handles undo */
3410 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3416 start_note_diff_command (_("paste"));
3418 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3419 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3420 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3421 const Evoral::MusicalTime duration = last_time - first_time;
3422 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3423 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3424 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3425 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3427 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3430 duration, pos, _region->position(),
3435 for (int n = 0; n < (int) times; ++n) {
3437 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3439 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3440 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3442 /* make all newly added notes selected */
3444 note_diff_add_note (copied_note, true);
3445 end_point = copied_note->end_time();
3449 /* if we pasted past the current end of the region, extend the region */
3451 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3452 framepos_t region_end = _region->position() + _region->length() - 1;
3454 if (end_frame > region_end) {
3456 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3458 _region->clear_changes ();
3459 _region->set_length (end_frame - _region->position());
3460 trackview.session()->add_command (new StatefulDiffCommand (_region));
3466 struct EventNoteTimeEarlyFirstComparator {
3467 bool operator() (NoteBase* a, NoteBase* b) {
3468 return a->note()->time() < b->note()->time();
3473 MidiRegionView::time_sort_events ()
3475 if (!_sort_needed) {
3479 EventNoteTimeEarlyFirstComparator cmp;
3482 _sort_needed = false;
3486 MidiRegionView::goto_next_note (bool add_to_selection)
3488 bool use_next = false;
3490 if (_events.back()->selected()) {
3494 time_sort_events ();
3496 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3497 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3499 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3500 if ((*i)->selected()) {
3503 } else if (use_next) {
3504 if (channel_mask & (1 << (*i)->note()->channel())) {
3505 if (!add_to_selection) {
3508 note_selected (*i, true, false);
3515 /* use the first one */
3517 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3518 unique_select (_events.front());
3523 MidiRegionView::goto_previous_note (bool add_to_selection)
3525 bool use_next = false;
3527 if (_events.front()->selected()) {
3531 time_sort_events ();
3533 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3534 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3536 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3537 if ((*i)->selected()) {
3540 } else if (use_next) {
3541 if (channel_mask & (1 << (*i)->note()->channel())) {
3542 if (!add_to_selection) {
3545 note_selected (*i, true, false);
3552 /* use the last one */
3554 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3555 unique_select (*(_events.rbegin()));
3560 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3562 bool had_selected = false;
3564 time_sort_events ();
3566 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3567 if ((*i)->selected()) {
3568 selected.insert ((*i)->note());
3569 had_selected = true;
3573 if (allow_all_if_none_selected && !had_selected) {
3574 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3575 selected.insert ((*i)->note());
3581 MidiRegionView::update_ghost_note (double x, double y)
3583 x = std::max(0.0, x);
3585 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3590 _note_group->canvas_to_item (x, y);
3592 PublicEditor& editor = trackview.editor ();
3594 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3595 framecnt_t grid_frames;
3596 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3598 /* use region_frames... because we are converting a delta within the region
3601 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3603 /* note that this sets the time of the ghost note in beats relative to
3604 the start of the source; that is how all note times are stored.
3606 _ghost_note->note()->set_time (
3607 std::max(Evoral::MusicalTime(),
3608 absolute_frames_to_source_beats (f + _region->position ())));
3609 _ghost_note->note()->set_length (length);
3610 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3611 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3613 /* the ghost note does not appear in ghost regions, so pass false in here */
3614 update_note (_ghost_note, false);
3616 show_verbose_cursor (_ghost_note->note ());
3620 MidiRegionView::create_ghost_note (double x, double y)
3622 remove_ghost_note ();
3624 boost::shared_ptr<NoteType> g (new NoteType);
3625 _ghost_note = new Note (*this, _note_group, g);
3626 _ghost_note->set_ignore_events (true);
3627 _ghost_note->set_outline_color (0x000000aa);
3628 update_ghost_note (x, y);
3629 _ghost_note->show ();
3631 show_verbose_cursor (_ghost_note->note ());
3635 MidiRegionView::remove_ghost_note ()
3642 MidiRegionView::snap_changed ()
3648 create_ghost_note (_last_ghost_x, _last_ghost_y);
3652 MidiRegionView::drop_down_keys ()
3654 _mouse_state = None;
3658 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3660 /* XXX: This is dead code. What was it for? */
3662 double note = midi_stream_view()->y_to_note(y);
3664 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3666 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3668 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3669 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3670 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3671 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3676 bool add_mrv_selection = false;
3678 if (_selection.empty()) {
3679 add_mrv_selection = true;
3682 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3683 if (_selection.insert (*i).second) {
3684 (*i)->set_selected (true);
3688 if (add_mrv_selection) {
3689 PublicEditor& editor (trackview.editor());
3690 editor.get_selection().add (this);
3695 MidiRegionView::color_handler ()
3697 RegionView::color_handler ();
3699 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3700 (*i)->set_selected ((*i)->selected()); // will change color
3703 /* XXX probably more to do here */
3707 MidiRegionView::enable_display (bool yn)
3709 RegionView::enable_display (yn);
3716 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3718 if (_step_edit_cursor == 0) {
3719 ArdourCanvas::Item* const group = get_canvas_group();
3721 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3722 _step_edit_cursor->set_y0 (0);
3723 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3724 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3725 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3728 move_step_edit_cursor (pos);
3729 _step_edit_cursor->show ();
3733 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3735 _step_edit_cursor_position = pos;
3737 if (_step_edit_cursor) {
3738 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3739 _step_edit_cursor->set_x0 (pixel);
3740 set_step_edit_cursor_width (_step_edit_cursor_width);
3745 MidiRegionView::hide_step_edit_cursor ()
3747 if (_step_edit_cursor) {
3748 _step_edit_cursor->hide ();
3753 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3755 _step_edit_cursor_width = beats;
3757 if (_step_edit_cursor) {
3758 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3762 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3763 * @param w Source that the data will end up in.
3766 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3768 if (!_active_notes) {
3769 /* we aren't actively being recorded to */
3773 boost::shared_ptr<MidiSource> src = w.lock ();
3774 if (!src || src != midi_region()->midi_source()) {
3775 /* recorded data was not destined for our source */
3779 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3781 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3783 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3785 framepos_t back = max_framepos;
3787 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3788 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3790 if (ev.is_channel_event()) {
3791 if (get_channel_mode() == FilterChannels) {
3792 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3798 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3799 frames from the start of the source, and so time_beats is in terms of the
3803 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3805 if (ev.type() == MIDI_CMD_NOTE_ON) {
3806 boost::shared_ptr<NoteType> note (
3807 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3809 add_note (note, true);
3811 /* fix up our note range */
3812 if (ev.note() < _current_range_min) {
3813 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3814 } else if (ev.note() > _current_range_max) {
3815 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3818 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3819 resolve_note (ev.note (), time_beats);
3825 midi_stream_view()->check_record_layers (region(), back);
3829 MidiRegionView::trim_front_starting ()
3831 /* Reparent the note group to the region view's parent, so that it doesn't change
3832 when the region view is trimmed.
3834 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3835 _temporary_note_group->move (group->position ());
3836 _note_group->reparent (_temporary_note_group);
3840 MidiRegionView::trim_front_ending ()
3842 _note_group->reparent (group);
3843 delete _temporary_note_group;
3844 _temporary_note_group = 0;
3846 if (_region->start() < 0) {
3847 /* Trim drag made start time -ve; fix this */
3848 midi_region()->fix_negative_start ();
3853 MidiRegionView::edit_patch_change (PatchChange* pc)
3855 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3857 int response = d.run();
3860 case Gtk::RESPONSE_ACCEPT:
3862 case Gtk::RESPONSE_REJECT:
3863 delete_patch_change (pc);
3869 change_patch_change (pc->patch(), d.patch ());
3873 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3876 // sysyex object doesn't have a pointer to a sysex event
3877 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3878 // c->remove (sysex->sysex());
3879 // _model->apply_command (*trackview.session(), c);
3881 //_sys_exes.clear ();
3882 // display_sysexes();
3886 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3888 using namespace MIDI::Name;
3892 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3894 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3896 MIDI::Name::PatchPrimaryKey patch_key;
3897 get_patch_key_at(n->time(), n->channel(), patch_key);
3898 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3900 patch_key.bank_number,
3901 patch_key.program_number,
3907 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3909 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3910 (int) n->channel() + 1,
3911 (int) n->velocity());
3913 show_verbose_cursor(buf, 10, 20);
3917 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3919 trackview.editor().verbose_cursor()->set (text);
3920 trackview.editor().verbose_cursor()->show ();
3921 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3924 /** @param p A session framepos.
3925 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3926 * @return p snapped to the grid subdivision underneath it.
3929 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3931 PublicEditor& editor = trackview.editor ();
3933 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3935 grid_frames = region_beats_to_region_frames (grid_beats);
3937 /* Hack so that we always snap to the note that we are over, instead of snapping
3938 to the next one if we're more than halfway through the one we're over.
3940 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3941 p -= grid_frames / 2;
3944 return snap_frame_to_frame (p);
3947 /** Called when the selection has been cleared in any MidiRegionView.
3948 * @param rv MidiRegionView that the selection was cleared in.
3951 MidiRegionView::selection_cleared (MidiRegionView* rv)
3957 /* Clear our selection in sympathy; but don't signal the fact */
3958 clear_selection (false);
3962 MidiRegionView::note_button_release ()
3964 delete _note_player;
3969 MidiRegionView::get_channel_mode () const
3971 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3972 return rtav->midi_track()->get_playback_channel_mode();
3976 MidiRegionView::get_selected_channels () const
3978 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3979 return rtav->midi_track()->get_playback_channel_mask();
3984 MidiRegionView::get_grid_beats(framepos_t pos) const
3986 PublicEditor& editor = trackview.editor();
3987 bool success = false;
3988 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3990 beats = Evoral::MusicalTime(1);