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)
126 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
127 _note_group->raise_to_top();
128 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
130 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
131 connect_to_diskstream ();
133 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
135 PublicEditor& editor (trackview.editor());
136 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
139 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
140 RouteTimeAxisView& tv,
141 boost::shared_ptr<MidiRegion> r,
143 uint32_t basic_color,
145 TimeAxisViewItem::Visibility visibility)
146 : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
147 , _current_range_min(0)
148 , _current_range_max(0)
149 , _region_relative_time_converter(r->session().tempo_map(), r->position())
150 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
152 , _note_group (new ArdourCanvas::Container (parent))
153 , _note_diff_command (0)
155 , _step_edit_cursor (0)
156 , _step_edit_cursor_width (1.0)
157 , _step_edit_cursor_position (0.0)
158 , _channel_selection_scoped_note (0)
159 , _temporary_note_group (0)
162 , _sort_needed (true)
163 , _optimization_iterator (_events.end())
165 , _no_sound_notes (false)
168 , _grabbed_keyboard (false)
171 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
172 _note_group->raise_to_top();
174 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
176 connect_to_diskstream ();
178 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
180 PublicEditor& editor (trackview.editor());
181 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
185 MidiRegionView::parameter_changed (std::string const & p)
187 if (p == "display-first-midi-bank-as-zero") {
188 if (_enable_display) {
194 MidiRegionView::MidiRegionView (const MidiRegionView& other)
195 : sigc::trackable(other)
197 , _current_range_min(0)
198 , _current_range_max(0)
199 , _region_relative_time_converter(other.region_relative_time_converter())
200 , _source_relative_time_converter(other.source_relative_time_converter())
202 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
203 , _note_diff_command (0)
205 , _step_edit_cursor (0)
206 , _step_edit_cursor_width (1.0)
207 , _step_edit_cursor_position (0.0)
208 , _channel_selection_scoped_note (0)
209 , _temporary_note_group (0)
212 , _sort_needed (true)
213 , _optimization_iterator (_events.end())
215 , _no_sound_notes (false)
218 , _grabbed_keyboard (false)
224 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
225 : RegionView (other, boost::shared_ptr<Region> (region))
226 , _current_range_min(0)
227 , _current_range_max(0)
228 , _region_relative_time_converter(other.region_relative_time_converter())
229 , _source_relative_time_converter(other.source_relative_time_converter())
231 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
232 , _note_diff_command (0)
234 , _step_edit_cursor (0)
235 , _step_edit_cursor_width (1.0)
236 , _step_edit_cursor_position (0.0)
237 , _channel_selection_scoped_note (0)
238 , _temporary_note_group (0)
241 , _sort_needed (true)
242 , _optimization_iterator (_events.end())
244 , _no_sound_notes (false)
247 , _grabbed_keyboard (false)
254 MidiRegionView::init (bool wfd)
256 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
258 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
259 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
263 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
264 midi_region()->midi_source(0)->load_model(lm);
267 _model = midi_region()->midi_source(0)->model();
268 _enable_display = false;
269 fill_color_name = "midi frame base";
271 RegionView::init (false);
273 set_height (trackview.current_height());
276 region_sync_changed ();
277 region_resized (ARDOUR::bounds_change);
282 _enable_display = true;
285 display_model (_model);
289 reset_width_dependent_items (_pixel_width);
291 group->raise_to_top();
293 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
294 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
297 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
298 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
300 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
301 boost::bind (&MidiRegionView::snap_changed, this),
304 trackview.editor().MouseModeChanged.connect(_mouse_mode_connection, invalidator (*this),
305 boost::bind (&MidiRegionView::mouse_mode_changed, this),
308 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
309 connect_to_diskstream ();
311 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
313 PublicEditor& editor (trackview.editor());
314 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
318 MidiRegionView::instrument_info () const
320 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
321 return route_ui->route()->instrument_info();
324 const boost::shared_ptr<ARDOUR::MidiRegion>
325 MidiRegionView::midi_region() const
327 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
331 MidiRegionView::connect_to_diskstream ()
333 midi_view()->midi_track()->DataRecorded.connect(
334 *this, invalidator(*this),
335 boost::bind (&MidiRegionView::data_recorded, this, _1),
340 MidiRegionView::canvas_group_event(GdkEvent* ev)
342 if (in_destructor || _recregion) {
346 if (!trackview.editor().internal_editing()) {
347 // not in internal edit mode, so just act like a normal region
348 return RegionView::canvas_group_event (ev);
354 case GDK_ENTER_NOTIFY:
355 _last_event_x = ev->crossing.x;
356 _last_event_y = ev->crossing.y;
357 enter_notify(&ev->crossing);
358 // set entered_regionview (among other things)
359 return RegionView::canvas_group_event (ev);
361 case GDK_LEAVE_NOTIFY:
362 _last_event_x = ev->crossing.x;
363 _last_event_y = ev->crossing.y;
364 leave_notify(&ev->crossing);
365 // reset entered_regionview (among other things)
366 return RegionView::canvas_group_event (ev);
369 if (scroll (&ev->scroll)) {
375 return key_press (&ev->key);
377 case GDK_KEY_RELEASE:
378 return key_release (&ev->key);
380 case GDK_BUTTON_PRESS:
381 return button_press (&ev->button);
383 case GDK_BUTTON_RELEASE:
384 r = button_release (&ev->button);
385 _note_player.reset();
388 case GDK_MOTION_NOTIFY:
389 _last_event_x = ev->motion.x;
390 _last_event_y = ev->motion.y;
391 return motion (&ev->motion);
397 return RegionView::canvas_group_event (ev);
401 MidiRegionView::enter_notify (GdkEventCrossing* ev)
410 MidiRegionView::leave_notify (GdkEventCrossing*)
419 MidiRegionView::mouse_mode_changed ()
421 // Adjust frame colour (become more transparent for internal tools)
425 if (trackview.editor().internal_editing()) {
426 // Switched in to internal editing mode while entered
429 // Switched out of internal editing mode while entered
436 MidiRegionView::enter_internal()
438 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
439 // Show ghost note under pencil
440 create_ghost_note(_last_event_x, _last_event_y);
443 if (!_selection.empty()) {
444 // Grab keyboard for moving selected notes with arrow keys
445 Keyboard::magic_widget_grab_focus();
446 _grabbed_keyboard = true;
449 // Lower frame handles below notes so they don't steal events
450 if (frame_handle_start) {
451 frame_handle_start->lower_to_bottom();
453 if (frame_handle_end) {
454 frame_handle_end->lower_to_bottom();
459 MidiRegionView::leave_internal()
461 trackview.editor().verbose_cursor()->hide ();
462 remove_ghost_note ();
464 if (_grabbed_keyboard) {
465 Keyboard::magic_widget_drop_focus();
466 _grabbed_keyboard = false;
469 // Raise frame handles above notes so they catch events
470 if (frame_handle_start) {
471 frame_handle_start->raise_to_top();
473 if (frame_handle_end) {
474 frame_handle_end->raise_to_top();
479 MidiRegionView::button_press (GdkEventButton* ev)
481 if (ev->button != 1) {
485 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
486 MouseMode m = editor->current_mouse_mode();
488 if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
489 _press_cursor_ctx = CursorContext::create(*editor, editor->cursors()->midi_pencil);
492 if (_mouse_state != SelectTouchDragging) {
494 _pressed_button = ev->button;
495 _mouse_state = Pressed;
500 _pressed_button = ev->button;
506 MidiRegionView::button_release (GdkEventButton* ev)
508 double event_x, event_y;
510 if (ev->button != 1) {
517 group->canvas_to_item (event_x, event_y);
520 PublicEditor& editor = trackview.editor ();
522 _press_cursor_ctx.reset();
524 switch (_mouse_state) {
525 case Pressed: // Clicked
527 switch (editor.current_mouse_mode()) {
529 /* no motion occured - simple click */
538 if (Keyboard::is_insert_note_event(ev)) {
540 double event_x, event_y;
544 group->canvas_to_item (event_x, event_y);
546 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
548 /* Shorten the length by 1 tick so that we can add a new note at the next
549 grid snap without it overlapping this one.
551 beats -= Evoral::MusicalTime::tick();
553 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
560 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
562 /* Shorten the length by 1 tick so that we can add a new note at the next
563 grid snap without it overlapping this one.
565 beats -= Evoral::MusicalTime::tick();
567 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
578 case SelectRectDragging:
580 editor.drags()->end_grab ((GdkEvent *) ev);
582 create_ghost_note (ev->x, ev->y);
594 MidiRegionView::motion (GdkEventMotion* ev)
596 PublicEditor& editor = trackview.editor ();
598 if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
599 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
600 _mouse_state != AddDragging) {
602 create_ghost_note (ev->x, ev->y);
604 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
605 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
607 update_ghost_note (ev->x, ev->y);
609 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
611 remove_ghost_note ();
612 editor.verbose_cursor()->hide ();
614 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
616 update_ghost_note (ev->x, ev->y);
619 /* any motion immediately hides velocity text that may have been visible */
621 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
622 (*i)->hide_velocity ();
625 switch (_mouse_state) {
628 if (_pressed_button == 1) {
630 MouseMode m = editor.current_mouse_mode();
632 if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
633 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
634 _mouse_state = AddDragging;
635 remove_ghost_note ();
636 editor.verbose_cursor()->hide ();
638 } else if (m == MouseContent) {
639 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
640 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
643 _mouse_state = SelectRectDragging;
645 } else if (m == MouseRange) {
646 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
647 _mouse_state = SelectVerticalDragging;
654 case SelectRectDragging:
655 case SelectVerticalDragging:
657 editor.drags()->motion_handler ((GdkEvent *) ev, false);
660 case SelectTouchDragging:
668 /* we may be dragging some non-note object (eg. patch-change, sysex)
671 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
676 MidiRegionView::scroll (GdkEventScroll* ev)
678 if (_selection.empty()) {
682 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
683 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
684 it still works for zoom.
689 trackview.editor().verbose_cursor()->hide ();
691 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
692 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
694 if (ev->direction == GDK_SCROLL_UP) {
695 change_velocities (true, fine, false, together);
696 } else if (ev->direction == GDK_SCROLL_DOWN) {
697 change_velocities (false, fine, false, together);
699 /* left, right: we don't use them */
707 MidiRegionView::key_press (GdkEventKey* ev)
709 /* since GTK bindings are generally activated on press, and since
710 detectable auto-repeat is the name of the game and only sends
711 repeated presses, carry out key actions at key press, not release.
714 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
716 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
717 _mouse_state = SelectTouchDragging;
720 } else if (ev->keyval == GDK_Escape && unmodified) {
724 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
726 bool start = (ev->keyval == GDK_comma);
727 bool end = (ev->keyval == GDK_period);
728 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
729 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
731 change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
735 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
737 if (_selection.empty()) {
744 } else if (ev->keyval == GDK_Tab) {
746 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
747 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
749 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
753 } else if (ev->keyval == GDK_ISO_Left_Tab) {
755 /* Shift-TAB generates ISO Left Tab, for some reason */
757 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
758 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
760 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
766 } else if (ev->keyval == GDK_Up) {
768 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
769 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
770 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
772 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
773 change_velocities (true, fine, allow_smush, together);
775 transpose (true, fine, allow_smush);
779 } else if (ev->keyval == GDK_Down) {
781 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
782 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
783 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
785 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
786 change_velocities (false, fine, allow_smush, together);
788 transpose (false, fine, allow_smush);
792 } else if (ev->keyval == GDK_Left) {
794 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
795 nudge_notes (false, fine);
798 } else if (ev->keyval == GDK_Right) {
800 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
801 nudge_notes (true, fine);
804 } else if (ev->keyval == GDK_c && unmodified) {
808 } else if (ev->keyval == GDK_v && unmodified) {
817 MidiRegionView::key_release (GdkEventKey* ev)
819 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
827 MidiRegionView::channel_edit ()
829 if (_selection.empty()) {
833 /* pick a note somewhat at random (since Selection is a set<>) to
834 * provide the "current" channel for the dialog.
837 uint8_t current_channel = (*_selection.begin())->note()->channel ();
838 MidiChannelDialog channel_dialog (current_channel);
839 int ret = channel_dialog.run ();
842 case Gtk::RESPONSE_OK:
848 uint8_t new_channel = channel_dialog.active_channel ();
850 start_note_diff_command (_("channel edit"));
852 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
853 Selection::iterator next = i;
855 change_note_channel (*i, new_channel);
863 MidiRegionView::velocity_edit ()
865 if (_selection.empty()) {
869 /* pick a note somewhat at random (since Selection is a set<>) to
870 * provide the "current" velocity for the dialog.
873 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
874 MidiVelocityDialog velocity_dialog (current_velocity);
875 int ret = velocity_dialog.run ();
878 case Gtk::RESPONSE_OK:
884 uint8_t new_velocity = velocity_dialog.velocity ();
886 start_note_diff_command (_("velocity edit"));
888 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
889 Selection::iterator next = i;
891 change_note_velocity (*i, new_velocity, false);
899 MidiRegionView::show_list_editor ()
902 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
904 _list_editor->present ();
907 /** Add a note to the model, and the view, at a canvas (click) coordinate.
908 * \param t time in frames relative to the position of the region
909 * \param y vertical position in pixels
910 * \param length duration of the note in beats
911 * \param snap_t true to snap t to the grid, otherwise false.
914 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
916 if (length < 2 * DBL_EPSILON) {
920 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
921 MidiStreamView* const view = mtv->midi_view();
923 const double note = view->y_to_note(y);
925 // Start of note in frames relative to region start
927 framecnt_t grid_frames;
928 t = snap_frame_to_grid_underneath (t, grid_frames);
931 const boost::shared_ptr<NoteType> new_note (
932 new NoteType (mtv->get_channel_for_add (),
933 region_frames_to_region_beats(t + _region->start()),
935 (uint8_t)note, 0x40));
937 if (_model->contains (new_note)) {
941 view->update_note_range(new_note->note());
943 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
945 _model->apply_command(*trackview.session(), cmd);
947 play_midi_note (new_note);
951 MidiRegionView::clear_events (bool with_selection_signal)
953 clear_selection (with_selection_signal);
956 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
957 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
962 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
967 _patch_changes.clear();
969 _optimization_iterator = _events.end();
973 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
977 content_connection.disconnect ();
978 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
982 if (_enable_display) {
988 MidiRegionView::start_note_diff_command (string name)
990 if (!_note_diff_command) {
991 _note_diff_command = _model->new_note_diff_command (name);
996 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
998 if (_note_diff_command) {
999 _note_diff_command->add (note);
1002 _marked_for_selection.insert(note);
1004 if (show_velocity) {
1005 _marked_for_velocity.insert(note);
1010 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1012 if (_note_diff_command && ev->note()) {
1013 _note_diff_command->remove(ev->note());
1018 MidiRegionView::note_diff_add_change (NoteBase* ev,
1019 MidiModel::NoteDiffCommand::Property property,
1022 if (_note_diff_command) {
1023 _note_diff_command->change (ev->note(), property, val);
1028 MidiRegionView::note_diff_add_change (NoteBase* ev,
1029 MidiModel::NoteDiffCommand::Property property,
1030 Evoral::MusicalTime val)
1032 if (_note_diff_command) {
1033 _note_diff_command->change (ev->note(), property, val);
1038 MidiRegionView::apply_diff (bool as_subcommand)
1042 if (!_note_diff_command) {
1046 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1047 // Mark all selected notes for selection when model reloads
1048 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1049 _marked_for_selection.insert((*i)->note());
1053 if (as_subcommand) {
1054 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1056 _model->apply_command (*trackview.session(), _note_diff_command);
1059 _note_diff_command = 0;
1060 midi_view()->midi_track()->playlist_modified();
1062 if (add_or_remove) {
1063 _marked_for_selection.clear();
1066 _marked_for_velocity.clear();
1070 MidiRegionView::abort_command()
1072 delete _note_diff_command;
1073 _note_diff_command = 0;
1078 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1080 if (_optimization_iterator != _events.end()) {
1081 ++_optimization_iterator;
1084 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1085 return *_optimization_iterator;
1088 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1089 if ((*_optimization_iterator)->note() == note) {
1090 return *_optimization_iterator;
1098 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1100 MidiModel::Notes notes;
1101 _model->get_notes (notes, op, val, chan_mask);
1103 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1104 NoteBase* cne = find_canvas_note (*n);
1112 MidiRegionView::redisplay_model()
1114 // Don't redisplay the model if we're currently recording and displaying that
1115 if (_active_notes) {
1123 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1124 (*i)->invalidate ();
1127 MidiModel::ReadLock lock(_model->read_lock());
1129 MidiModel::Notes& notes (_model->notes());
1130 _optimization_iterator = _events.begin();
1132 bool empty_when_starting = _events.empty();
1134 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1136 boost::shared_ptr<NoteType> note (*n);
1140 if (note_in_region_range (note, visible)) {
1142 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1149 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1151 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1163 add_note (note, visible);
1168 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1176 /* remove note items that are no longer valid */
1178 if (!empty_when_starting) {
1179 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1180 if (!(*i)->valid ()) {
1182 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1183 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1185 gr->remove_note (*i);
1190 i = _events.erase (i);
1198 _patch_changes.clear();
1202 display_patch_changes ();
1204 _marked_for_selection.clear ();
1205 _marked_for_velocity.clear ();
1207 /* we may have caused _events to contain things out of order (e.g. if a note
1208 moved earlier or later). we don't generally need them in time order, but
1209 make a note that a sort is required for those cases that require it.
1212 _sort_needed = true;
1216 MidiRegionView::display_patch_changes ()
1218 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1219 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1221 for (uint8_t i = 0; i < 16; ++i) {
1222 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1226 /** @param active_channel true to display patch changes fully, false to display
1227 * them `greyed-out' (as on an inactive channel)
1230 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1232 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1234 if ((*i)->channel() != channel) {
1238 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1239 add_canvas_patch_change (*i, patch_name, active_channel);
1244 MidiRegionView::display_sysexes()
1246 bool have_periodic_system_messages = false;
1247 bool display_periodic_messages = true;
1249 if (!ARDOUR_UI::config()->get_never_display_periodic_midi()) {
1251 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1252 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1253 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1256 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1257 have_periodic_system_messages = true;
1263 if (have_periodic_system_messages) {
1264 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1266 /* get an approximate value for the number of samples per video frame */
1268 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1270 /* if we are zoomed out beyond than the cutoff (i.e. more
1271 * frames per pixel than frames per 4 video frames), don't
1272 * show periodic sysex messages.
1275 if (zoom > (video_frame*4)) {
1276 display_periodic_messages = false;
1280 display_periodic_messages = false;
1283 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1285 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1286 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1288 Evoral::MusicalTime time = (*i)->time();
1291 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1292 if (!display_periodic_messages) {
1300 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1301 str << int((*i)->buffer()[b]);
1302 if (b != (*i)->size() -1) {
1306 string text = str.str();
1308 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1310 double height = midi_stream_view()->contents_height();
1312 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1313 // SysEx canvas object!!!
1315 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1316 new SysEx (*this, _note_group, text, height, x, 1.0));
1318 // Show unless message is beyond the region bounds
1319 if (time - _region->start() >= _region->length() || time < _region->start()) {
1325 _sys_exes.push_back(sysex);
1329 MidiRegionView::~MidiRegionView ()
1331 in_destructor = true;
1333 trackview.editor().verbose_cursor()->hide ();
1335 note_delete_connection.disconnect ();
1337 delete _list_editor;
1339 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1341 if (_active_notes) {
1345 _selection_cleared_connection.disconnect ();
1348 clear_events (false);
1351 delete _note_diff_command;
1352 delete _step_edit_cursor;
1353 delete _temporary_note_group;
1357 MidiRegionView::region_resized (const PropertyChange& what_changed)
1359 RegionView::region_resized(what_changed);
1361 if (what_changed.contains (ARDOUR::Properties::position)) {
1362 _region_relative_time_converter.set_origin_b(_region->position());
1363 set_duration(_region->length(), 0);
1364 if (_enable_display) {
1369 if (what_changed.contains (ARDOUR::Properties::start) ||
1370 what_changed.contains (ARDOUR::Properties::position)) {
1371 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1376 MidiRegionView::reset_width_dependent_items (double pixel_width)
1378 RegionView::reset_width_dependent_items(pixel_width);
1380 if (_enable_display) {
1384 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1385 if ((*x)->canvas_item()->width() >= _pixel_width) {
1392 move_step_edit_cursor (_step_edit_cursor_position);
1393 set_step_edit_cursor_width (_step_edit_cursor_width);
1397 MidiRegionView::set_height (double height)
1399 double old_height = _height;
1400 RegionView::set_height(height);
1402 apply_note_range (midi_stream_view()->lowest_note(),
1403 midi_stream_view()->highest_note(),
1404 height != old_height);
1407 name_text->raise_to_top();
1410 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1411 (*x)->set_height (midi_stream_view()->contents_height());
1414 if (_step_edit_cursor) {
1415 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1420 /** Apply the current note range from the stream view
1421 * by repositioning/hiding notes as necessary
1424 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1426 if (!_enable_display) {
1430 if (!force && _current_range_min == min && _current_range_max == max) {
1434 _current_range_min = min;
1435 _current_range_max = max;
1437 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1438 NoteBase* event = *i;
1439 boost::shared_ptr<NoteType> note (event->note());
1441 if (note->note() < _current_range_min ||
1442 note->note() > _current_range_max) {
1448 if (Note* cnote = dynamic_cast<Note*>(event)) {
1450 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1451 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1456 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1463 MidiRegionView::add_ghost (TimeAxisView& tv)
1467 double unit_position = _region->position () / samples_per_pixel;
1468 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1469 MidiGhostRegion* ghost;
1471 if (mtv && mtv->midi_view()) {
1472 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1473 to allow having midi notes on top of note lines and waveforms.
1475 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1477 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1480 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1481 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1482 ghost->add_note(note);
1486 ghost->set_height ();
1487 ghost->set_duration (_region->length() / samples_per_pixel);
1488 ghosts.push_back (ghost);
1490 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1496 /** Begin tracking note state for successive calls to add_event
1499 MidiRegionView::begin_write()
1501 if (_active_notes) {
1502 delete[] _active_notes;
1504 _active_notes = new Note*[128];
1505 for (unsigned i = 0; i < 128; ++i) {
1506 _active_notes[i] = 0;
1511 /** Destroy note state for add_event
1514 MidiRegionView::end_write()
1516 delete[] _active_notes;
1518 _marked_for_selection.clear();
1519 _marked_for_velocity.clear();
1523 /** Resolve an active MIDI note (while recording).
1526 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1528 if (midi_view()->note_mode() != Sustained) {
1532 if (_active_notes && _active_notes[note]) {
1534 /* XXX is end_time really region-centric? I think so, because
1535 this is a new region that we're recording, so source zero is
1536 the same as region zero
1538 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1540 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1541 _active_notes[note]->set_outline_all ();
1542 _active_notes[note] = 0;
1548 /** Extend active notes to rightmost edge of region (if length is changed)
1551 MidiRegionView::extend_active_notes()
1553 if (!_active_notes) {
1557 for (unsigned i=0; i < 128; ++i) {
1558 if (_active_notes[i]) {
1559 _active_notes[i]->set_x1(
1560 trackview.editor().sample_to_pixel(_region->position() + _region->length()));
1567 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1569 if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1573 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1575 if (!route_ui || !route_ui->midi_track()) {
1579 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1583 /* NotePlayer deletes itself */
1587 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1589 const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1590 start_playing_midi_chord(notes);
1594 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1596 if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1600 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1602 if (!route_ui || !route_ui->midi_track()) {
1606 _note_player = boost::shared_ptr<NotePlayer>(new NotePlayer(route_ui->midi_track()));
1608 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1609 _note_player->add (*n);
1612 _note_player->on ();
1617 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1619 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1620 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1622 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1623 (note->note() <= midi_stream_view()->highest_note());
1628 /** Update a canvas note's size from its model note.
1629 * @param ev Canvas note to update.
1630 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1633 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1635 boost::shared_ptr<NoteType> note = ev->note();
1636 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1637 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1642 /* trim note display to not overlap the end of its region */
1644 if (note->length() > 0) {
1645 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1646 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1648 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1651 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1653 if (!note->length()) {
1654 if (_active_notes && note->note() < 128) {
1655 // If this note is already active there's a stuck note,
1656 // finish the old note rectangle
1657 if (_active_notes[note->note()]) {
1658 Note* const old_rect = _active_notes[note->note()];
1659 boost::shared_ptr<NoteType> old_note = old_rect->note();
1660 old_rect->set_x1 (x);
1661 old_rect->set_outline_all ();
1663 _active_notes[note->note()] = ev;
1665 /* outline all but right edge */
1666 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1667 ArdourCanvas::Rectangle::TOP|
1668 ArdourCanvas::Rectangle::LEFT|
1669 ArdourCanvas::Rectangle::BOTTOM));
1671 /* outline all edges */
1672 ev->set_outline_all ();
1675 // Update color in case velocity has changed
1676 ev->set_fill_color(ev->base_color());
1677 ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1679 if (update_ghost_regions) {
1680 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1681 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1683 gr->update_note (ev);
1690 MidiRegionView::update_hit (Hit* ev)
1692 boost::shared_ptr<NoteType> note = ev->note();
1694 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1695 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1696 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1697 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1699 ev->set_position (ArdourCanvas::Duple (x, y));
1700 ev->set_height (diamond_size);
1703 /** Add a MIDI note to the view (with length).
1705 * If in sustained mode, notes with length 0 will be considered active
1706 * notes, and resolve_note should be called when the corresponding note off
1707 * event arrives, to properly display the note.
1710 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1712 NoteBase* event = 0;
1714 if (midi_view()->note_mode() == Sustained) {
1716 Note* ev_rect = new Note (*this, _note_group, note);
1718 update_note (ev_rect);
1722 MidiGhostRegion* gr;
1724 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1725 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1726 gr->add_note(ev_rect);
1730 } else if (midi_view()->note_mode() == Percussive) {
1732 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1734 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1736 update_hit (ev_diamond);
1745 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1746 note_selected(event, true);
1749 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1750 event->show_velocity();
1753 event->on_channel_selection_change (get_selected_channels());
1754 _events.push_back(event);
1763 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1764 MidiStreamView* const view = mtv->midi_view();
1766 view->update_note_range (note->note());
1770 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1771 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1773 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1775 /* potentially extend region to hold new note */
1777 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1778 framepos_t region_end = _region->last_frame();
1780 if (end_frame > region_end) {
1781 _region->set_length (end_frame - _region->position());
1784 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1785 MidiStreamView* const view = mtv->midi_view();
1787 view->update_note_range(new_note->note());
1789 _marked_for_selection.clear ();
1792 start_note_diff_command (_("step add"));
1793 note_diff_add_note (new_note, true, false);
1796 // last_step_edit_note = new_note;
1800 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1802 change_note_lengths (false, false, beats, false, true);
1805 /** Add a new patch change flag to the canvas.
1806 * @param patch the patch change to add
1807 * @param the text to display in the flag
1808 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1811 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1813 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1814 const double x = trackview.editor().sample_to_pixel (region_frames);
1816 double const height = midi_stream_view()->contents_height();
1818 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1819 // so we need to do something more sophisticated to keep its color
1820 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1823 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1824 new PatchChange(*this, group,
1831 if (patch_change->item().width() < _pixel_width) {
1832 // Show unless patch change is beyond the region bounds
1833 if (region_frames < 0 || region_frames >= _region->length()) {
1834 patch_change->hide();
1836 patch_change->show();
1839 patch_change->hide ();
1842 _patch_changes.push_back (patch_change);
1845 MIDI::Name::PatchPrimaryKey
1846 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1848 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1851 /// Return true iff @p pc applies to the given time on the given channel.
1853 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1855 return pc->time() <= time && pc->channel() == channel;
1859 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1861 // The earliest event not before time
1862 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1864 // Go backwards until we find the latest PC for this channel, or the start
1865 while (i != _model->patch_changes().begin() &&
1866 (i == _model->patch_changes().end() ||
1867 !patch_applies(*i, time, channel))) {
1871 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1872 key.set_bank((*i)->bank());
1873 key.set_program((*i)->program ());
1881 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1883 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1885 if (pc.patch()->program() != new_patch.program()) {
1886 c->change_program (pc.patch (), new_patch.program());
1889 int const new_bank = new_patch.bank();
1890 if (pc.patch()->bank() != new_bank) {
1891 c->change_bank (pc.patch (), new_bank);
1894 _model->apply_command (*trackview.session(), c);
1896 _patch_changes.clear ();
1897 display_patch_changes ();
1901 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1903 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1905 if (old_change->time() != new_change.time()) {
1906 c->change_time (old_change, new_change.time());
1909 if (old_change->channel() != new_change.channel()) {
1910 c->change_channel (old_change, new_change.channel());
1913 if (old_change->program() != new_change.program()) {
1914 c->change_program (old_change, new_change.program());
1917 if (old_change->bank() != new_change.bank()) {
1918 c->change_bank (old_change, new_change.bank());
1921 _model->apply_command (*trackview.session(), c);
1923 _patch_changes.clear ();
1924 display_patch_changes ();
1927 /** Add a patch change to the region.
1928 * @param t Time in frames relative to region position
1929 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1930 * MidiTimeAxisView::get_channel_for_add())
1933 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1935 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1937 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1938 c->add (MidiModel::PatchChangePtr (
1939 new Evoral::PatchChange<Evoral::MusicalTime> (
1940 absolute_frames_to_source_beats (_region->position() + t),
1941 mtv->get_channel_for_add(), patch.program(), patch.bank()
1946 _model->apply_command (*trackview.session(), c);
1948 _patch_changes.clear ();
1949 display_patch_changes ();
1953 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1955 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1956 c->change_time (pc.patch (), t);
1957 _model->apply_command (*trackview.session(), c);
1959 _patch_changes.clear ();
1960 display_patch_changes ();
1964 MidiRegionView::delete_patch_change (PatchChange* pc)
1966 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1967 c->remove (pc->patch ());
1968 _model->apply_command (*trackview.session(), c);
1970 _patch_changes.clear ();
1971 display_patch_changes ();
1975 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
1977 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
1979 key.set_bank(key.bank() + delta);
1981 key.set_program(key.program() + delta);
1983 change_patch_change(patch, key);
1987 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
1989 if (_selection.empty()) {
1993 _selection.erase (cne);
1997 MidiRegionView::delete_selection()
1999 if (_selection.empty()) {
2003 start_note_diff_command (_("delete selection"));
2005 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2006 if ((*i)->selected()) {
2007 _note_diff_command->remove((*i)->note());
2017 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2019 start_note_diff_command (_("delete note"));
2020 _note_diff_command->remove (n);
2023 trackview.editor().verbose_cursor()->hide ();
2027 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2029 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2031 Selection::iterator tmp = i;
2034 (*i)->set_selected (false);
2035 (*i)->hide_velocity ();
2036 _selection.erase (i);
2044 if (!ev && _entered) {
2045 // Clearing selection entirely, ungrab keyboard
2046 Keyboard::magic_widget_drop_focus();
2047 _grabbed_keyboard = false;
2050 /* this does not change the status of this regionview w.r.t the editor
2055 SelectionCleared (this); /* EMIT SIGNAL */
2060 MidiRegionView::unique_select(NoteBase* ev)
2062 const bool selection_was_empty = _selection.empty();
2064 clear_selection_except (ev);
2066 /* don't bother with checking to see if we should remove this
2067 regionview from the editor selection, since we're about to add
2068 another note, and thus put/keep this regionview in the editor
2072 if (!ev->selected()) {
2073 add_to_selection (ev);
2074 if (selection_was_empty && _entered) {
2075 // Grab keyboard for moving notes with arrow keys
2076 Keyboard::magic_widget_grab_focus();
2077 _grabbed_keyboard = true;
2083 MidiRegionView::select_all_notes ()
2087 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2088 add_to_selection (*i);
2093 MidiRegionView::select_range (framepos_t start, framepos_t end)
2097 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2098 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2099 if (t >= start && t <= end) {
2100 add_to_selection (*i);
2106 MidiRegionView::invert_selection ()
2108 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2109 if ((*i)->selected()) {
2110 remove_from_selection(*i);
2112 add_to_selection (*i);
2118 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2120 bool have_selection = !_selection.empty();
2121 uint8_t low_note = 127;
2122 uint8_t high_note = 0;
2123 MidiModel::Notes& notes (_model->notes());
2124 _optimization_iterator = _events.begin();
2126 if (extend && !have_selection) {
2130 /* scan existing selection to get note range */
2132 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2133 if ((*i)->note()->note() < low_note) {
2134 low_note = (*i)->note()->note();
2136 if ((*i)->note()->note() > high_note) {
2137 high_note = (*i)->note()->note();
2144 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2145 /* only note previously selected is the one we are
2146 * reselecting. treat this as cancelling the selection.
2153 low_note = min (low_note, notenum);
2154 high_note = max (high_note, notenum);
2157 _no_sound_notes = true;
2159 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2161 boost::shared_ptr<NoteType> note (*n);
2163 bool select = false;
2165 if (((1 << note->channel()) & channel_mask) != 0) {
2167 if ((note->note() >= low_note && note->note() <= high_note)) {
2170 } else if (note->note() == notenum) {
2176 if ((cne = find_canvas_note (note)) != 0) {
2177 // extend is false because we've taken care of it,
2178 // since it extends by time range, not pitch.
2179 note_selected (cne, add, false);
2183 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2187 _no_sound_notes = false;
2191 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2193 MidiModel::Notes& notes (_model->notes());
2194 _optimization_iterator = _events.begin();
2196 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2198 boost::shared_ptr<NoteType> note (*n);
2201 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2202 if ((cne = find_canvas_note (note)) != 0) {
2203 if (cne->selected()) {
2204 note_deselected (cne);
2206 note_selected (cne, true, false);
2214 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2217 clear_selection_except (ev);
2218 if (!_selection.empty()) {
2219 PublicEditor& editor (trackview.editor());
2220 editor.get_selection().add (this);
2226 if (!ev->selected()) {
2227 add_to_selection (ev);
2231 /* find end of latest note selected, select all between that and the start of "ev" */
2233 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2234 Evoral::MusicalTime latest = Evoral::MusicalTime();
2236 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2237 if ((*i)->note()->end_time() > latest) {
2238 latest = (*i)->note()->end_time();
2240 if ((*i)->note()->time() < earliest) {
2241 earliest = (*i)->note()->time();
2245 if (ev->note()->end_time() > latest) {
2246 latest = ev->note()->end_time();
2249 if (ev->note()->time() < earliest) {
2250 earliest = ev->note()->time();
2253 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2255 /* find notes entirely within OR spanning the earliest..latest range */
2257 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2258 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2259 add_to_selection (*i);
2267 MidiRegionView::note_deselected(NoteBase* ev)
2269 remove_from_selection (ev);
2273 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2275 PublicEditor& editor = trackview.editor();
2277 // Convert to local coordinates
2278 const framepos_t p = _region->position();
2279 const double y = midi_view()->y_position();
2280 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2281 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2282 const double y0 = max(0.0, gy0 - y);
2283 const double y1 = max(0.0, gy1 - y);
2285 // TODO: Make this faster by storing the last updated selection rect, and only
2286 // adjusting things that are in the area that appears/disappeared.
2287 // We probably need a tree to be able to find events in O(log(n)) time.
2289 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2290 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2291 // Rectangles intersect
2292 if (!(*i)->selected()) {
2293 add_to_selection (*i);
2295 } else if ((*i)->selected() && !extend) {
2296 // Rectangles do not intersect
2297 remove_from_selection (*i);
2301 typedef RouteTimeAxisView::AutomationTracks ATracks;
2302 typedef std::list<Selectable*> Selectables;
2304 /* Add control points to selection. */
2305 const ATracks& atracks = midi_view()->automation_tracks();
2306 Selectables selectables;
2307 editor.get_selection().clear_points();
2308 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2309 a->second->get_selectables(start, end, gy0, gy1, selectables);
2310 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2311 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2313 editor.get_selection().add(cp);
2316 a->second->set_selected_points(editor.get_selection().points);
2321 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2327 // TODO: Make this faster by storing the last updated selection rect, and only
2328 // adjusting things that are in the area that appears/disappeared.
2329 // We probably need a tree to be able to find events in O(log(n)) time.
2331 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2332 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2333 // within y- (note-) range
2334 if (!(*i)->selected()) {
2335 add_to_selection (*i);
2337 } else if ((*i)->selected() && !extend) {
2338 remove_from_selection (*i);
2344 MidiRegionView::remove_from_selection (NoteBase* ev)
2346 Selection::iterator i = _selection.find (ev);
2348 if (i != _selection.end()) {
2349 _selection.erase (i);
2350 if (_selection.empty() && _grabbed_keyboard) {
2352 Keyboard::magic_widget_drop_focus();
2353 _grabbed_keyboard = false;
2357 ev->set_selected (false);
2358 ev->hide_velocity ();
2360 if (_selection.empty()) {
2361 PublicEditor& editor (trackview.editor());
2362 editor.get_selection().remove (this);
2367 MidiRegionView::add_to_selection (NoteBase* ev)
2369 const bool selection_was_empty = _selection.empty();
2371 if (_selection.insert (ev).second) {
2372 ev->set_selected (true);
2373 start_playing_midi_note ((ev)->note());
2374 if (selection_was_empty && _entered) {
2375 // Grab keyboard for moving notes with arrow keys
2376 Keyboard::magic_widget_grab_focus();
2377 _grabbed_keyboard = true;
2381 if (selection_was_empty) {
2382 PublicEditor& editor (trackview.editor());
2383 editor.get_selection().add (this);
2388 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2390 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2391 PossibleChord to_play;
2392 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2394 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2395 if ((*i)->note()->time() < earliest) {
2396 earliest = (*i)->note()->time();
2400 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2401 if ((*i)->note()->time() == earliest) {
2402 to_play.push_back ((*i)->note());
2404 (*i)->move_event(dx, dy);
2407 if (dy && !_selection.empty() && !_no_sound_notes && ARDOUR_UI::config()->get_sound_midi_notes()) {
2409 if (to_play.size() > 1) {
2411 PossibleChord shifted;
2413 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2414 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2415 moved_note->set_note (moved_note->note() + cumulative_dy);
2416 shifted.push_back (moved_note);
2419 start_playing_midi_chord (shifted);
2421 } else if (!to_play.empty()) {
2423 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2424 moved_note->set_note (moved_note->note() + cumulative_dy);
2425 start_playing_midi_note (moved_note);
2431 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2433 uint8_t lowest_note_in_selection = 127;
2434 uint8_t highest_note_in_selection = 0;
2435 uint8_t highest_note_difference = 0;
2437 // find highest and lowest notes first
2439 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2440 uint8_t pitch = (*i)->note()->note();
2441 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2442 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2446 cerr << "dnote: " << (int) dnote << endl;
2447 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2448 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2449 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2450 << int(highest_note_in_selection) << endl;
2451 cerr << "selection size: " << _selection.size() << endl;
2452 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2455 // Make sure the note pitch does not exceed the MIDI standard range
2456 if (highest_note_in_selection + dnote > 127) {
2457 highest_note_difference = highest_note_in_selection - 127;
2460 start_note_diff_command (_("move notes"));
2462 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2464 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2465 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2471 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2473 uint8_t original_pitch = (*i)->note()->note();
2474 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2476 // keep notes in standard midi range
2477 clamp_to_0_127(new_pitch);
2479 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2480 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2482 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2487 // care about notes being moved beyond the upper/lower bounds on the canvas
2488 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2489 highest_note_in_selection > midi_stream_view()->highest_note()) {
2490 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2494 /** @param x Pixel relative to the region position.
2495 * @return Snapped frame relative to the region position.
2498 MidiRegionView::snap_pixel_to_sample(double x)
2500 PublicEditor& editor (trackview.editor());
2501 return snap_frame_to_frame (editor.pixel_to_sample (x));
2504 /** @param x Pixel relative to the region position.
2505 * @return Snapped pixel relative to the region position.
2508 MidiRegionView::snap_to_pixel(double x)
2510 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2514 MidiRegionView::get_position_pixels()
2516 framepos_t region_frame = get_position();
2517 return trackview.editor().sample_to_pixel(region_frame);
2521 MidiRegionView::get_end_position_pixels()
2523 framepos_t frame = get_position() + get_duration ();
2524 return trackview.editor().sample_to_pixel(frame);
2528 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2530 /* the time converter will return the frame corresponding to `beats'
2531 relative to the start of the source. The start of the source
2532 is an implied position given by region->position - region->start
2534 const framepos_t source_start = _region->position() - _region->start();
2535 return source_start + _source_relative_time_converter.to (beats);
2539 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2541 /* the `frames' argument needs to be converted into a frame count
2542 relative to the start of the source before being passed in to the
2545 const framepos_t source_start = _region->position() - _region->start();
2546 return _source_relative_time_converter.from (frames - source_start);
2550 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2552 return _region_relative_time_converter.to(beats);
2556 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2558 return _region_relative_time_converter.from(frames);
2562 MidiRegionView::begin_resizing (bool /*at_front*/)
2564 _resize_data.clear();
2566 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2567 Note *note = dynamic_cast<Note*> (*i);
2569 // only insert CanvasNotes into the map
2571 NoteResizeData *resize_data = new NoteResizeData();
2572 resize_data->note = note;
2574 // create a new SimpleRect from the note which will be the resize preview
2575 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2576 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2578 // calculate the colors: get the color settings
2579 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2580 ARDOUR_UI::config()->color ("midi note selected"),
2583 // make the resize preview notes more transparent and bright
2584 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2586 // calculate color based on note velocity
2587 resize_rect->set_fill_color (UINT_INTERPOLATE(
2588 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2592 resize_rect->set_outline_color (NoteBase::calculate_outline (
2593 ARDOUR_UI::config()->color ("midi note selected")));
2595 resize_data->resize_rect = resize_rect;
2596 _resize_data.push_back(resize_data);
2601 /** Update resizing notes while user drags.
2602 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2603 * @param at_front which end of the note (true == note on, false == note off)
2604 * @param delta_x change in mouse position since the start of the drag
2605 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2606 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2607 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2608 * as the \a primary note.
2611 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2613 bool cursor_set = false;
2615 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2616 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2617 Note* canvas_note = (*i)->note;
2622 current_x = canvas_note->x0() + delta_x;
2624 current_x = primary->x0() + delta_x;
2628 current_x = canvas_note->x1() + delta_x;
2630 current_x = primary->x1() + delta_x;
2634 if (current_x < 0) {
2635 // This works even with snapping because RegionView::snap_frame_to_frame()
2636 // snaps forward if the snapped sample is before the beginning of the region
2639 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2640 current_x = trackview.editor().sample_to_pixel(_region->length());
2644 resize_rect->set_x0 (snap_to_pixel(current_x));
2645 resize_rect->set_x1 (canvas_note->x1());
2647 resize_rect->set_x1 (snap_to_pixel(current_x));
2648 resize_rect->set_x0 (canvas_note->x0());
2652 const double snapped_x = snap_pixel_to_sample (current_x);
2653 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2654 Evoral::MusicalTime len = Evoral::MusicalTime();
2657 if (beats < canvas_note->note()->end_time()) {
2658 len = canvas_note->note()->time() - beats;
2659 len += canvas_note->note()->length();
2662 if (beats >= canvas_note->note()->time()) {
2663 len = beats - canvas_note->note()->time();
2668 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2669 show_verbose_cursor (buf, 0, 0);
2678 /** Finish resizing notes when the user releases the mouse button.
2679 * Parameters the same as for \a update_resizing().
2682 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2684 start_note_diff_command (_("resize notes"));
2686 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2687 Note* canvas_note = (*i)->note;
2688 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2690 /* Get the new x position for this resize, which is in pixels relative
2691 * to the region position.
2698 current_x = canvas_note->x0() + delta_x;
2700 current_x = primary->x0() + delta_x;
2704 current_x = canvas_note->x1() + delta_x;
2706 current_x = primary->x1() + delta_x;
2710 if (current_x < 0) {
2713 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2714 current_x = trackview.editor().sample_to_pixel(_region->length());
2717 /* Convert that to a frame within the source */
2718 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2720 /* and then to beats */
2721 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2723 if (at_front && x_beats < canvas_note->note()->end_time()) {
2724 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2726 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2727 len += canvas_note->note()->length();
2730 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2735 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2738 /* XXX convert to beats */
2739 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2747 _resize_data.clear();
2752 MidiRegionView::abort_resizing ()
2754 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2755 delete (*i)->resize_rect;
2759 _resize_data.clear ();
2763 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2765 uint8_t new_velocity;
2768 new_velocity = event->note()->velocity() + velocity;
2769 clamp_to_0_127(new_velocity);
2771 new_velocity = velocity;
2774 event->set_selected (event->selected()); // change color
2776 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2780 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2785 new_note = event->note()->note() + note;
2790 clamp_to_0_127 (new_note);
2791 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2795 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2797 bool change_start = false;
2798 bool change_length = false;
2799 Evoral::MusicalTime new_start;
2800 Evoral::MusicalTime new_length;
2802 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2804 front_delta: if positive - move the start of the note later in time (shortening it)
2805 if negative - move the start of the note earlier in time (lengthening it)
2807 end_delta: if positive - move the end of the note later in time (lengthening it)
2808 if negative - move the end of the note earlier in time (shortening it)
2811 if (!!front_delta) {
2812 if (front_delta < 0) {
2814 if (event->note()->time() < -front_delta) {
2815 new_start = Evoral::MusicalTime();
2817 new_start = event->note()->time() + front_delta; // moves earlier
2820 /* start moved toward zero, so move the end point out to where it used to be.
2821 Note that front_delta is negative, so this increases the length.
2824 new_length = event->note()->length() - front_delta;
2825 change_start = true;
2826 change_length = true;
2830 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2832 if (new_pos < event->note()->end_time()) {
2833 new_start = event->note()->time() + front_delta;
2834 /* start moved toward the end, so move the end point back to where it used to be */
2835 new_length = event->note()->length() - front_delta;
2836 change_start = true;
2837 change_length = true;
2844 bool can_change = true;
2845 if (end_delta < 0) {
2846 if (event->note()->length() < -end_delta) {
2852 new_length = event->note()->length() + end_delta;
2853 change_length = true;
2858 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2861 if (change_length) {
2862 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2867 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2869 uint8_t new_channel;
2873 if (event->note()->channel() < -chn) {
2876 new_channel = event->note()->channel() + chn;
2879 new_channel = event->note()->channel() + chn;
2882 new_channel = (uint8_t) chn;
2885 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2889 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2891 Evoral::MusicalTime new_time;
2895 if (event->note()->time() < -delta) {
2896 new_time = Evoral::MusicalTime();
2898 new_time = event->note()->time() + delta;
2901 new_time = event->note()->time() + delta;
2907 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2911 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2913 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2917 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2922 if (_selection.empty()) {
2937 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2938 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2944 start_note_diff_command (_("change velocities"));
2946 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2947 Selection::iterator next = i;
2951 if (i == _selection.begin()) {
2952 change_note_velocity (*i, delta, true);
2953 value = (*i)->note()->velocity() + delta;
2955 change_note_velocity (*i, value, false);
2959 change_note_velocity (*i, delta, true);
2968 if (!_selection.empty()) {
2970 snprintf (buf, sizeof (buf), "Vel %d",
2971 (int) (*_selection.begin())->note()->velocity());
2972 show_verbose_cursor (buf, 10, 10);
2978 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2980 if (_selection.empty()) {
2997 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2999 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3003 if ((int8_t) (*i)->note()->note() + delta > 127) {
3010 start_note_diff_command (_("transpose"));
3012 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3013 Selection::iterator next = i;
3015 change_note_note (*i, delta, true);
3023 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3027 delta = Evoral::MusicalTime(1.0/128.0);
3029 /* grab the current grid distance */
3030 delta = get_grid_beats(_region->position());
3038 start_note_diff_command (_("change note lengths"));
3040 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3041 Selection::iterator next = i;
3044 /* note the negation of the delta for start */
3047 (start ? -delta : Evoral::MusicalTime()),
3048 (end ? delta : Evoral::MusicalTime()));
3057 MidiRegionView::nudge_notes (bool forward, bool fine)
3059 if (_selection.empty()) {
3063 /* pick a note as the point along the timeline to get the nudge distance.
3064 its not necessarily the earliest note, so we may want to pull the notes out
3065 into a vector and sort before using the first one.
3068 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3069 Evoral::MusicalTime delta;
3073 /* non-fine, move by 1 bar regardless of snap */
3074 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3076 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3078 /* grid is off - use nudge distance */
3081 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3082 delta = region_frames_to_region_beats (fabs ((double)distance));
3088 framepos_t next_pos = ref_point;
3091 if (max_framepos - 1 < next_pos) {
3095 if (next_pos == 0) {
3101 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3102 const framecnt_t distance = ref_point - next_pos;
3103 delta = region_frames_to_region_beats (fabs ((double)distance));
3114 start_note_diff_command (_("nudge"));
3116 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3117 Selection::iterator next = i;
3119 change_note_time (*i, delta, true);
3127 MidiRegionView::change_channel(uint8_t channel)
3129 start_note_diff_command(_("change channel"));
3130 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3131 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3139 MidiRegionView::note_entered(NoteBase* ev)
3141 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3143 if (_mouse_state == SelectTouchDragging) {
3144 note_selected (ev, true);
3145 } else if (editor->current_mouse_mode() == MouseContent) {
3146 show_verbose_cursor (ev->note ());
3147 } else if (editor->current_mouse_mode() == MouseDraw) {
3148 show_verbose_cursor (ev->note ());
3153 MidiRegionView::note_left (NoteBase*)
3155 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3157 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3158 (*i)->hide_velocity ();
3161 editor->verbose_cursor()->hide ();
3165 MidiRegionView::patch_entered (PatchChange* p)
3168 /* XXX should get patch name if we can */
3169 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3170 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3171 << _("Channel ") << ((int) p->patch()->channel() + 1);
3172 show_verbose_cursor (s.str(), 10, 20);
3173 p->item().grab_focus();
3177 MidiRegionView::patch_left (PatchChange *)
3179 trackview.editor().verbose_cursor()->hide ();
3180 /* focus will transfer back via the enter-notify event sent to this
3186 MidiRegionView::sysex_entered (SysEx* p)
3190 // need a way to extract text from p->_flag->_text
3192 // show_verbose_cursor (s.str(), 10, 20);
3193 p->item().grab_focus();
3197 MidiRegionView::sysex_left (SysEx *)
3199 trackview.editor().verbose_cursor()->hide ();
3200 /* focus will transfer back via the enter-notify event sent to this
3206 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3208 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3209 Editing::MouseMode mm = editor->current_mouse_mode();
3210 bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3212 Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3213 if (can_set_cursor && ctx) {
3214 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3215 ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3216 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3217 ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3219 ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3225 MidiRegionView::get_fill_color() const
3227 const std::string mod_name = (_dragging ? "dragging region" :
3228 trackview.editor().internal_editing() ? "editable region" :
3231 return ARDOUR_UI::config()->color_mod ("selected region base", mod_name);
3232 } else if (high_enough_for_name || !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3233 return ARDOUR_UI::config()->color_mod ("midi frame base", mod_name);
3235 return ARDOUR_UI::config()->color_mod (fill_color, mod_name);
3239 MidiRegionView::midi_channel_mode_changed ()
3241 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3242 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3243 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3245 if (mode == ForceChannel) {
3246 mask = 0xFFFF; // Show all notes as active (below)
3249 // Update notes for selection
3250 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3251 (*i)->on_channel_selection_change (mask);
3254 _patch_changes.clear ();
3255 display_patch_changes ();
3259 MidiRegionView::instrument_settings_changed ()
3265 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3267 if (_selection.empty()) {
3271 PublicEditor& editor (trackview.editor());
3275 /* XXX what to do ? */
3279 editor.get_cut_buffer().add (selection_as_cut_buffer());
3287 start_note_diff_command();
3289 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3296 note_diff_remove_note (*i);
3306 MidiRegionView::selection_as_cut_buffer () const
3310 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3311 NoteType* n = (*i)->note().get();
3312 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3315 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3321 /** This method handles undo */
3323 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3325 // Paste notes, if available
3326 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3327 if (m != selection.midi_notes.end()) {
3328 ctx.counts.increase_n_notes();
3329 paste_internal(pos, ctx.count, ctx.times, **m);
3332 // Paste control points to automation children, if available
3333 typedef RouteTimeAxisView::AutomationTracks ATracks;
3334 const ATracks& atracks = midi_view()->automation_tracks();
3335 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3336 a->second->paste(pos, selection, ctx);
3342 /** This method handles undo */
3344 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3350 start_note_diff_command (_("paste"));
3352 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3353 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3354 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3355 const Evoral::MusicalTime duration = last_time - first_time;
3356 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3357 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3358 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3359 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3361 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3364 duration, pos, _region->position(),
3369 for (int n = 0; n < (int) times; ++n) {
3371 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3373 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3374 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3376 /* make all newly added notes selected */
3378 note_diff_add_note (copied_note, true);
3379 end_point = copied_note->end_time();
3383 /* if we pasted past the current end of the region, extend the region */
3385 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3386 framepos_t region_end = _region->position() + _region->length() - 1;
3388 if (end_frame > region_end) {
3390 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3392 _region->clear_changes ();
3393 _region->set_length (end_frame - _region->position());
3394 trackview.session()->add_command (new StatefulDiffCommand (_region));
3400 struct EventNoteTimeEarlyFirstComparator {
3401 bool operator() (NoteBase* a, NoteBase* b) {
3402 return a->note()->time() < b->note()->time();
3407 MidiRegionView::time_sort_events ()
3409 if (!_sort_needed) {
3413 EventNoteTimeEarlyFirstComparator cmp;
3416 _sort_needed = false;
3420 MidiRegionView::goto_next_note (bool add_to_selection)
3422 bool use_next = false;
3424 if (_events.back()->selected()) {
3428 time_sort_events ();
3430 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3431 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3433 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3434 if ((*i)->selected()) {
3437 } else if (use_next) {
3438 if (channel_mask & (1 << (*i)->note()->channel())) {
3439 if (!add_to_selection) {
3442 note_selected (*i, true, false);
3449 /* use the first one */
3451 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3452 unique_select (_events.front());
3457 MidiRegionView::goto_previous_note (bool add_to_selection)
3459 bool use_next = false;
3461 if (_events.front()->selected()) {
3465 time_sort_events ();
3467 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3468 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3470 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3471 if ((*i)->selected()) {
3474 } else if (use_next) {
3475 if (channel_mask & (1 << (*i)->note()->channel())) {
3476 if (!add_to_selection) {
3479 note_selected (*i, true, false);
3486 /* use the last one */
3488 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3489 unique_select (*(_events.rbegin()));
3494 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3496 bool had_selected = false;
3498 time_sort_events ();
3500 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3501 if ((*i)->selected()) {
3502 selected.insert ((*i)->note());
3503 had_selected = true;
3507 if (allow_all_if_none_selected && !had_selected) {
3508 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3509 selected.insert ((*i)->note());
3515 MidiRegionView::update_ghost_note (double x, double y)
3517 x = std::max(0.0, x);
3519 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3524 _note_group->canvas_to_item (x, y);
3526 PublicEditor& editor = trackview.editor ();
3528 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3529 framecnt_t grid_frames;
3530 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3532 /* use region_frames... because we are converting a delta within the region
3535 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3537 /* note that this sets the time of the ghost note in beats relative to
3538 the start of the source; that is how all note times are stored.
3540 _ghost_note->note()->set_time (
3541 std::max(Evoral::MusicalTime(),
3542 absolute_frames_to_source_beats (f + _region->position ())));
3543 _ghost_note->note()->set_length (length);
3544 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3545 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3547 /* the ghost note does not appear in ghost regions, so pass false in here */
3548 update_note (_ghost_note, false);
3550 show_verbose_cursor (_ghost_note->note ());
3554 MidiRegionView::create_ghost_note (double x, double y)
3556 remove_ghost_note ();
3558 boost::shared_ptr<NoteType> g (new NoteType);
3559 _ghost_note = new Note (*this, _note_group, g);
3560 _ghost_note->set_ignore_events (true);
3561 _ghost_note->set_outline_color (0x000000aa);
3562 update_ghost_note (x, y);
3563 _ghost_note->show ();
3565 show_verbose_cursor (_ghost_note->note ());
3569 MidiRegionView::remove_ghost_note ()
3576 MidiRegionView::snap_changed ()
3582 create_ghost_note (_last_ghost_x, _last_ghost_y);
3586 MidiRegionView::drop_down_keys ()
3588 _mouse_state = None;
3592 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3594 /* XXX: This is dead code. What was it for? */
3596 double note = midi_stream_view()->y_to_note(y);
3598 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3600 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3602 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3603 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3604 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3605 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3610 bool add_mrv_selection = false;
3612 if (_selection.empty()) {
3613 add_mrv_selection = true;
3616 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3617 if (_selection.insert (*i).second) {
3618 (*i)->set_selected (true);
3622 if (add_mrv_selection) {
3623 PublicEditor& editor (trackview.editor());
3624 editor.get_selection().add (this);
3629 MidiRegionView::color_handler ()
3631 RegionView::color_handler ();
3633 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3634 (*i)->set_selected ((*i)->selected()); // will change color
3637 /* XXX probably more to do here */
3641 MidiRegionView::enable_display (bool yn)
3643 RegionView::enable_display (yn);
3650 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3652 if (_step_edit_cursor == 0) {
3653 ArdourCanvas::Item* const group = get_canvas_group();
3655 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3656 _step_edit_cursor->set_y0 (0);
3657 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3658 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3659 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3662 move_step_edit_cursor (pos);
3663 _step_edit_cursor->show ();
3667 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3669 _step_edit_cursor_position = pos;
3671 if (_step_edit_cursor) {
3672 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3673 _step_edit_cursor->set_x0 (pixel);
3674 set_step_edit_cursor_width (_step_edit_cursor_width);
3679 MidiRegionView::hide_step_edit_cursor ()
3681 if (_step_edit_cursor) {
3682 _step_edit_cursor->hide ();
3687 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3689 _step_edit_cursor_width = beats;
3691 if (_step_edit_cursor) {
3692 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3696 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3697 * @param w Source that the data will end up in.
3700 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3702 if (!_active_notes) {
3703 /* we aren't actively being recorded to */
3707 boost::shared_ptr<MidiSource> src = w.lock ();
3708 if (!src || src != midi_region()->midi_source()) {
3709 /* recorded data was not destined for our source */
3713 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3715 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3717 framepos_t back = max_framepos;
3719 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3720 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3722 if (ev.is_channel_event()) {
3723 if (get_channel_mode() == FilterChannels) {
3724 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3730 /* convert from session frames to source beats */
3731 Evoral::MusicalTime const time_beats = _source_relative_time_converter.from(ev.time());
3733 if (ev.type() == MIDI_CMD_NOTE_ON) {
3734 boost::shared_ptr<NoteType> note (
3735 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3737 add_note (note, true);
3739 /* fix up our note range */
3740 if (ev.note() < _current_range_min) {
3741 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3742 } else if (ev.note() > _current_range_max) {
3743 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3746 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3747 resolve_note (ev.note (), time_beats);
3753 midi_stream_view()->check_record_layers (region(), back);
3757 MidiRegionView::trim_front_starting ()
3759 /* Reparent the note group to the region view's parent, so that it doesn't change
3760 when the region view is trimmed.
3762 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3763 _temporary_note_group->move (group->position ());
3764 _note_group->reparent (_temporary_note_group);
3768 MidiRegionView::trim_front_ending ()
3770 _note_group->reparent (group);
3771 delete _temporary_note_group;
3772 _temporary_note_group = 0;
3774 if (_region->start() < 0) {
3775 /* Trim drag made start time -ve; fix this */
3776 midi_region()->fix_negative_start ();
3781 MidiRegionView::edit_patch_change (PatchChange* pc)
3783 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3785 int response = d.run();
3788 case Gtk::RESPONSE_ACCEPT:
3790 case Gtk::RESPONSE_REJECT:
3791 delete_patch_change (pc);
3797 change_patch_change (pc->patch(), d.patch ());
3801 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3804 // sysyex object doesn't have a pointer to a sysex event
3805 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3806 // c->remove (sysex->sysex());
3807 // _model->apply_command (*trackview.session(), c);
3809 //_sys_exes.clear ();
3810 // display_sysexes();
3814 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3816 using namespace MIDI::Name;
3820 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3822 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3824 MIDI::Name::PatchPrimaryKey patch_key;
3825 get_patch_key_at(n->time(), n->channel(), patch_key);
3826 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3829 patch_key.program(),
3835 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3837 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3838 (int) n->channel() + 1,
3839 (int) n->velocity());
3841 show_verbose_cursor(buf, 10, 20);
3845 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3847 trackview.editor().verbose_cursor()->set (text);
3848 trackview.editor().verbose_cursor()->show ();
3849 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3852 /** @param p A session framepos.
3853 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3854 * @return p snapped to the grid subdivision underneath it.
3857 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3859 PublicEditor& editor = trackview.editor ();
3861 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3863 grid_frames = region_beats_to_region_frames (grid_beats);
3865 /* Hack so that we always snap to the note that we are over, instead of snapping
3866 to the next one if we're more than halfway through the one we're over.
3868 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3869 p -= grid_frames / 2;
3872 return snap_frame_to_frame (p);
3875 /** Called when the selection has been cleared in any MidiRegionView.
3876 * @param rv MidiRegionView that the selection was cleared in.
3879 MidiRegionView::selection_cleared (MidiRegionView* rv)
3885 /* Clear our selection in sympathy; but don't signal the fact */
3886 clear_selection (false);
3890 MidiRegionView::note_button_release ()
3892 _note_player.reset();
3896 MidiRegionView::get_channel_mode () const
3898 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3899 return rtav->midi_track()->get_playback_channel_mode();
3903 MidiRegionView::get_selected_channels () const
3905 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3906 return rtav->midi_track()->get_playback_channel_mask();
3911 MidiRegionView::get_grid_beats(framepos_t pos) const
3913 PublicEditor& editor = trackview.editor();
3914 bool success = false;
3915 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3917 beats = Evoral::MusicalTime(1);