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 // Start of note in frames relative to region start
925 framecnt_t grid_frames;
926 t = snap_frame_to_grid_underneath (t, grid_frames);
929 const MidiModel::TimeType beat_time = region_frames_to_region_beats(
930 t + _region->start());
932 const double note = view->y_to_note(y);
933 const uint8_t chan = mtv->get_channel_for_add();
934 const uint8_t velocity = get_velocity_for_add(beat_time);
936 const boost::shared_ptr<NoteType> new_note(
937 new NoteType (chan, beat_time, length, (uint8_t)note, velocity));
939 if (_model->contains (new_note)) {
943 view->update_note_range(new_note->note());
945 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
947 _model->apply_command(*trackview.session(), cmd);
949 play_midi_note (new_note);
953 MidiRegionView::clear_events (bool with_selection_signal)
955 clear_selection (with_selection_signal);
958 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
959 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
964 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
969 _patch_changes.clear();
971 _optimization_iterator = _events.end();
975 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
979 content_connection.disconnect ();
980 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
984 if (_enable_display) {
990 MidiRegionView::start_note_diff_command (string name)
992 if (!_note_diff_command) {
993 _note_diff_command = _model->new_note_diff_command (name);
998 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1000 if (_note_diff_command) {
1001 _note_diff_command->add (note);
1004 _marked_for_selection.insert(note);
1006 if (show_velocity) {
1007 _marked_for_velocity.insert(note);
1012 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1014 if (_note_diff_command && ev->note()) {
1015 _note_diff_command->remove(ev->note());
1020 MidiRegionView::note_diff_add_change (NoteBase* ev,
1021 MidiModel::NoteDiffCommand::Property property,
1024 if (_note_diff_command) {
1025 _note_diff_command->change (ev->note(), property, val);
1030 MidiRegionView::note_diff_add_change (NoteBase* ev,
1031 MidiModel::NoteDiffCommand::Property property,
1032 Evoral::MusicalTime val)
1034 if (_note_diff_command) {
1035 _note_diff_command->change (ev->note(), property, val);
1040 MidiRegionView::apply_diff (bool as_subcommand)
1044 if (!_note_diff_command) {
1048 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1049 // Mark all selected notes for selection when model reloads
1050 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1051 _marked_for_selection.insert((*i)->note());
1055 if (as_subcommand) {
1056 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1058 _model->apply_command (*trackview.session(), _note_diff_command);
1061 _note_diff_command = 0;
1062 midi_view()->midi_track()->playlist_modified();
1064 if (add_or_remove) {
1065 _marked_for_selection.clear();
1068 _marked_for_velocity.clear();
1072 MidiRegionView::abort_command()
1074 delete _note_diff_command;
1075 _note_diff_command = 0;
1080 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1082 if (_optimization_iterator != _events.end()) {
1083 ++_optimization_iterator;
1086 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1087 return *_optimization_iterator;
1090 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1091 if ((*_optimization_iterator)->note() == note) {
1092 return *_optimization_iterator;
1100 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1102 MidiModel::Notes notes;
1103 _model->get_notes (notes, op, val, chan_mask);
1105 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1106 NoteBase* cne = find_canvas_note (*n);
1114 MidiRegionView::redisplay_model()
1116 // Don't redisplay the model if we're currently recording and displaying that
1117 if (_active_notes) {
1125 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1126 (*i)->invalidate ();
1129 MidiModel::ReadLock lock(_model->read_lock());
1131 MidiModel::Notes& notes (_model->notes());
1132 _optimization_iterator = _events.begin();
1134 bool empty_when_starting = _events.empty();
1136 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1138 boost::shared_ptr<NoteType> note (*n);
1142 if (note_in_region_range (note, visible)) {
1144 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1157 add_note (note, visible);
1162 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1170 /* remove note items that are no longer valid */
1172 if (!empty_when_starting) {
1173 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1174 if (!(*i)->valid ()) {
1176 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1177 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1179 gr->remove_note (*i);
1184 i = _events.erase (i);
1192 _patch_changes.clear();
1196 display_patch_changes ();
1198 _marked_for_selection.clear ();
1199 _marked_for_velocity.clear ();
1201 /* we may have caused _events to contain things out of order (e.g. if a note
1202 moved earlier or later). we don't generally need them in time order, but
1203 make a note that a sort is required for those cases that require it.
1206 _sort_needed = true;
1210 MidiRegionView::display_patch_changes ()
1212 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1213 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1215 for (uint8_t i = 0; i < 16; ++i) {
1216 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1220 /** @param active_channel true to display patch changes fully, false to display
1221 * them `greyed-out' (as on an inactive channel)
1224 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1226 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1228 if ((*i)->channel() != channel) {
1232 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1233 add_canvas_patch_change (*i, patch_name, active_channel);
1238 MidiRegionView::display_sysexes()
1240 bool have_periodic_system_messages = false;
1241 bool display_periodic_messages = true;
1243 if (!ARDOUR_UI::config()->get_never_display_periodic_midi()) {
1245 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1246 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1247 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1250 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1251 have_periodic_system_messages = true;
1257 if (have_periodic_system_messages) {
1258 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1260 /* get an approximate value for the number of samples per video frame */
1262 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1264 /* if we are zoomed out beyond than the cutoff (i.e. more
1265 * frames per pixel than frames per 4 video frames), don't
1266 * show periodic sysex messages.
1269 if (zoom > (video_frame*4)) {
1270 display_periodic_messages = false;
1274 display_periodic_messages = false;
1277 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1279 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1280 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1282 Evoral::MusicalTime time = (*i)->time();
1285 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1286 if (!display_periodic_messages) {
1294 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1295 str << int((*i)->buffer()[b]);
1296 if (b != (*i)->size() -1) {
1300 string text = str.str();
1302 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1304 double height = midi_stream_view()->contents_height();
1306 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1307 // SysEx canvas object!!!
1309 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1310 new SysEx (*this, _note_group, text, height, x, 1.0));
1312 // Show unless message is beyond the region bounds
1313 if (time - _region->start() >= _region->length() || time < _region->start()) {
1319 _sys_exes.push_back(sysex);
1323 MidiRegionView::~MidiRegionView ()
1325 in_destructor = true;
1327 trackview.editor().verbose_cursor()->hide ();
1329 note_delete_connection.disconnect ();
1331 delete _list_editor;
1333 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1335 if (_active_notes) {
1339 _selection_cleared_connection.disconnect ();
1342 clear_events (false);
1345 delete _note_diff_command;
1346 delete _step_edit_cursor;
1347 delete _temporary_note_group;
1351 MidiRegionView::region_resized (const PropertyChange& what_changed)
1353 RegionView::region_resized(what_changed);
1355 if (what_changed.contains (ARDOUR::Properties::position)) {
1356 _region_relative_time_converter.set_origin_b(_region->position());
1357 set_duration(_region->length(), 0);
1358 if (_enable_display) {
1363 if (what_changed.contains (ARDOUR::Properties::start) ||
1364 what_changed.contains (ARDOUR::Properties::position)) {
1365 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1370 MidiRegionView::reset_width_dependent_items (double pixel_width)
1372 RegionView::reset_width_dependent_items(pixel_width);
1374 if (_enable_display) {
1378 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1379 if ((*x)->canvas_item()->width() >= _pixel_width) {
1386 move_step_edit_cursor (_step_edit_cursor_position);
1387 set_step_edit_cursor_width (_step_edit_cursor_width);
1391 MidiRegionView::set_height (double height)
1393 double old_height = _height;
1394 RegionView::set_height(height);
1396 apply_note_range (midi_stream_view()->lowest_note(),
1397 midi_stream_view()->highest_note(),
1398 height != old_height);
1401 name_text->raise_to_top();
1404 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1405 (*x)->set_height (midi_stream_view()->contents_height());
1408 if (_step_edit_cursor) {
1409 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1414 /** Apply the current note range from the stream view
1415 * by repositioning/hiding notes as necessary
1418 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1420 if (!_enable_display) {
1424 if (!force && _current_range_min == min && _current_range_max == max) {
1428 _current_range_min = min;
1429 _current_range_max = max;
1431 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1432 NoteBase* event = *i;
1433 boost::shared_ptr<NoteType> note (event->note());
1435 if (note->note() < _current_range_min ||
1436 note->note() > _current_range_max) {
1442 if (Note* cnote = dynamic_cast<Note*>(event)) {
1444 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1445 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1450 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1457 MidiRegionView::add_ghost (TimeAxisView& tv)
1459 double unit_position = _region->position () / samples_per_pixel;
1460 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1461 MidiGhostRegion* ghost;
1463 if (mtv && mtv->midi_view()) {
1464 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1465 to allow having midi notes on top of note lines and waveforms.
1467 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1469 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1472 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1473 ghost->add_note(*i);
1476 ghost->set_height ();
1477 ghost->set_duration (_region->length() / samples_per_pixel);
1478 ghosts.push_back (ghost);
1480 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1486 /** Begin tracking note state for successive calls to add_event
1489 MidiRegionView::begin_write()
1491 if (_active_notes) {
1492 delete[] _active_notes;
1494 _active_notes = new Note*[128];
1495 for (unsigned i = 0; i < 128; ++i) {
1496 _active_notes[i] = 0;
1501 /** Destroy note state for add_event
1504 MidiRegionView::end_write()
1506 delete[] _active_notes;
1508 _marked_for_selection.clear();
1509 _marked_for_velocity.clear();
1513 /** Resolve an active MIDI note (while recording).
1516 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1518 if (midi_view()->note_mode() != Sustained) {
1522 if (_active_notes && _active_notes[note]) {
1524 /* XXX is end_time really region-centric? I think so, because
1525 this is a new region that we're recording, so source zero is
1526 the same as region zero
1528 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1530 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1531 _active_notes[note]->set_outline_all ();
1532 _active_notes[note] = 0;
1538 /** Extend active notes to rightmost edge of region (if length is changed)
1541 MidiRegionView::extend_active_notes()
1543 if (!_active_notes) {
1547 for (unsigned i=0; i < 128; ++i) {
1548 if (_active_notes[i]) {
1549 _active_notes[i]->set_x1(
1550 trackview.editor().sample_to_pixel(_region->position() + _region->length()));
1557 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1559 if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1563 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1565 if (!route_ui || !route_ui->midi_track()) {
1569 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1573 /* NotePlayer deletes itself */
1577 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1579 const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1580 start_playing_midi_chord(notes);
1584 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1586 if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1590 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1592 if (!route_ui || !route_ui->midi_track()) {
1596 _note_player = boost::shared_ptr<NotePlayer>(new NotePlayer(route_ui->midi_track()));
1598 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1599 _note_player->add (*n);
1602 _note_player->on ();
1607 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1609 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1610 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1612 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1613 (note->note() <= midi_stream_view()->highest_note());
1619 MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
1623 if ((sus = dynamic_cast<Note*>(note))) {
1624 update_sustained(sus, update_ghost_regions);
1625 } else if ((hit = dynamic_cast<Hit*>(note))) {
1626 update_hit(hit, update_ghost_regions);
1630 /** Update a canvas note's size from its model note.
1631 * @param ev Canvas note to update.
1632 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1635 MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
1637 boost::shared_ptr<NoteType> note = ev->note();
1638 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1639 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1644 /* trim note display to not overlap the end of its region */
1646 if (note->length() > 0) {
1647 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1648 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1650 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1653 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1655 if (!note->length()) {
1656 if (_active_notes && note->note() < 128) {
1657 // If this note is already active there's a stuck note,
1658 // finish the old note rectangle
1659 if (_active_notes[note->note()]) {
1660 Note* const old_rect = _active_notes[note->note()];
1661 boost::shared_ptr<NoteType> old_note = old_rect->note();
1662 old_rect->set_x1 (x);
1663 old_rect->set_outline_all ();
1665 _active_notes[note->note()] = ev;
1667 /* outline all but right edge */
1668 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1669 ArdourCanvas::Rectangle::TOP|
1670 ArdourCanvas::Rectangle::LEFT|
1671 ArdourCanvas::Rectangle::BOTTOM));
1673 /* outline all edges */
1674 ev->set_outline_all ();
1677 // Update color in case velocity has changed
1678 ev->set_fill_color(ev->base_color());
1679 ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1681 if (update_ghost_regions) {
1682 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1683 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1685 gr->update_note (ev);
1692 MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
1694 boost::shared_ptr<NoteType> note = ev->note();
1696 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1697 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1698 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1699 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1701 ev->set_position (ArdourCanvas::Duple (x, y));
1702 ev->set_height (diamond_size);
1704 // Update color in case velocity has changed
1705 ev->set_fill_color(ev->base_color());
1706 ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1708 if (update_ghost_regions) {
1709 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1710 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1712 gr->update_note (ev);
1718 /** Add a MIDI note to the view (with length).
1720 * If in sustained mode, notes with length 0 will be considered active
1721 * notes, and resolve_note should be called when the corresponding note off
1722 * event arrives, to properly display the note.
1725 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1727 NoteBase* event = 0;
1729 if (midi_view()->note_mode() == Sustained) {
1731 Note* ev_rect = new Note (*this, _note_group, note);
1733 update_note (ev_rect);
1737 } else if (midi_view()->note_mode() == Percussive) {
1739 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1741 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1743 update_hit (ev_diamond);
1752 MidiGhostRegion* gr;
1754 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1755 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1756 gr->add_note(event);
1760 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1761 note_selected(event, true);
1764 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1765 event->show_velocity();
1768 event->on_channel_selection_change (get_selected_channels());
1769 _events.push_back(event);
1778 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1779 MidiStreamView* const view = mtv->midi_view();
1781 view->update_note_range (note->note());
1785 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1786 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1788 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1790 /* potentially extend region to hold new note */
1792 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1793 framepos_t region_end = _region->last_frame();
1795 if (end_frame > region_end) {
1796 _region->set_length (end_frame - _region->position());
1799 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1800 MidiStreamView* const view = mtv->midi_view();
1802 view->update_note_range(new_note->note());
1804 _marked_for_selection.clear ();
1807 start_note_diff_command (_("step add"));
1808 note_diff_add_note (new_note, true, false);
1811 // last_step_edit_note = new_note;
1815 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1817 change_note_lengths (false, false, beats, false, true);
1820 /** Add a new patch change flag to the canvas.
1821 * @param patch the patch change to add
1822 * @param the text to display in the flag
1823 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1826 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1828 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1829 const double x = trackview.editor().sample_to_pixel (region_frames);
1831 double const height = midi_stream_view()->contents_height();
1833 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1834 // so we need to do something more sophisticated to keep its color
1835 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1838 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1839 new PatchChange(*this, group,
1846 if (patch_change->item().width() < _pixel_width) {
1847 // Show unless patch change is beyond the region bounds
1848 if (region_frames < 0 || region_frames >= _region->length()) {
1849 patch_change->hide();
1851 patch_change->show();
1854 patch_change->hide ();
1857 _patch_changes.push_back (patch_change);
1860 MIDI::Name::PatchPrimaryKey
1861 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1863 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1866 /// Return true iff @p pc applies to the given time on the given channel.
1868 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1870 return pc->time() <= time && pc->channel() == channel;
1874 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1876 // The earliest event not before time
1877 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1879 // Go backwards until we find the latest PC for this channel, or the start
1880 while (i != _model->patch_changes().begin() &&
1881 (i == _model->patch_changes().end() ||
1882 !patch_applies(*i, time, channel))) {
1886 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1887 key.set_bank((*i)->bank());
1888 key.set_program((*i)->program ());
1896 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1898 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1900 if (pc.patch()->program() != new_patch.program()) {
1901 c->change_program (pc.patch (), new_patch.program());
1904 int const new_bank = new_patch.bank();
1905 if (pc.patch()->bank() != new_bank) {
1906 c->change_bank (pc.patch (), new_bank);
1909 _model->apply_command (*trackview.session(), c);
1911 _patch_changes.clear ();
1912 display_patch_changes ();
1916 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1918 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1920 if (old_change->time() != new_change.time()) {
1921 c->change_time (old_change, new_change.time());
1924 if (old_change->channel() != new_change.channel()) {
1925 c->change_channel (old_change, new_change.channel());
1928 if (old_change->program() != new_change.program()) {
1929 c->change_program (old_change, new_change.program());
1932 if (old_change->bank() != new_change.bank()) {
1933 c->change_bank (old_change, new_change.bank());
1936 _model->apply_command (*trackview.session(), c);
1938 _patch_changes.clear ();
1939 display_patch_changes ();
1942 /** Add a patch change to the region.
1943 * @param t Time in frames relative to region position
1944 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1945 * MidiTimeAxisView::get_channel_for_add())
1948 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1950 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1952 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1953 c->add (MidiModel::PatchChangePtr (
1954 new Evoral::PatchChange<Evoral::MusicalTime> (
1955 absolute_frames_to_source_beats (_region->position() + t),
1956 mtv->get_channel_for_add(), patch.program(), patch.bank()
1961 _model->apply_command (*trackview.session(), c);
1963 _patch_changes.clear ();
1964 display_patch_changes ();
1968 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1970 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1971 c->change_time (pc.patch (), t);
1972 _model->apply_command (*trackview.session(), c);
1974 _patch_changes.clear ();
1975 display_patch_changes ();
1979 MidiRegionView::delete_patch_change (PatchChange* pc)
1981 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1982 c->remove (pc->patch ());
1983 _model->apply_command (*trackview.session(), c);
1985 _patch_changes.clear ();
1986 display_patch_changes ();
1990 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
1992 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
1994 key.set_bank(key.bank() + delta);
1996 key.set_program(key.program() + delta);
1998 change_patch_change(patch, key);
2002 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2004 if (_selection.empty()) {
2008 _selection.erase (cne);
2012 MidiRegionView::delete_selection()
2014 if (_selection.empty()) {
2018 start_note_diff_command (_("delete selection"));
2020 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2021 if ((*i)->selected()) {
2022 _note_diff_command->remove((*i)->note());
2032 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2034 start_note_diff_command (_("delete note"));
2035 _note_diff_command->remove (n);
2038 trackview.editor().verbose_cursor()->hide ();
2042 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2044 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2046 Selection::iterator tmp = i;
2049 (*i)->set_selected (false);
2050 (*i)->hide_velocity ();
2051 _selection.erase (i);
2059 if (!ev && _entered) {
2060 // Clearing selection entirely, ungrab keyboard
2061 Keyboard::magic_widget_drop_focus();
2062 _grabbed_keyboard = false;
2065 /* this does not change the status of this regionview w.r.t the editor
2070 SelectionCleared (this); /* EMIT SIGNAL */
2075 MidiRegionView::unique_select(NoteBase* ev)
2077 const bool selection_was_empty = _selection.empty();
2079 clear_selection_except (ev);
2081 /* don't bother with checking to see if we should remove this
2082 regionview from the editor selection, since we're about to add
2083 another note, and thus put/keep this regionview in the editor
2087 if (!ev->selected()) {
2088 add_to_selection (ev);
2089 if (selection_was_empty && _entered) {
2090 // Grab keyboard for moving notes with arrow keys
2091 Keyboard::magic_widget_grab_focus();
2092 _grabbed_keyboard = true;
2098 MidiRegionView::select_all_notes ()
2102 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2103 add_to_selection (*i);
2108 MidiRegionView::select_range (framepos_t start, framepos_t end)
2112 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2113 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2114 if (t >= start && t <= end) {
2115 add_to_selection (*i);
2121 MidiRegionView::invert_selection ()
2123 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2124 if ((*i)->selected()) {
2125 remove_from_selection(*i);
2127 add_to_selection (*i);
2133 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2135 bool have_selection = !_selection.empty();
2136 uint8_t low_note = 127;
2137 uint8_t high_note = 0;
2138 MidiModel::Notes& notes (_model->notes());
2139 _optimization_iterator = _events.begin();
2141 if (extend && !have_selection) {
2145 /* scan existing selection to get note range */
2147 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2148 if ((*i)->note()->note() < low_note) {
2149 low_note = (*i)->note()->note();
2151 if ((*i)->note()->note() > high_note) {
2152 high_note = (*i)->note()->note();
2159 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2160 /* only note previously selected is the one we are
2161 * reselecting. treat this as cancelling the selection.
2168 low_note = min (low_note, notenum);
2169 high_note = max (high_note, notenum);
2172 _no_sound_notes = true;
2174 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2176 boost::shared_ptr<NoteType> note (*n);
2178 bool select = false;
2180 if (((1 << note->channel()) & channel_mask) != 0) {
2182 if ((note->note() >= low_note && note->note() <= high_note)) {
2185 } else if (note->note() == notenum) {
2191 if ((cne = find_canvas_note (note)) != 0) {
2192 // extend is false because we've taken care of it,
2193 // since it extends by time range, not pitch.
2194 note_selected (cne, add, false);
2198 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2202 _no_sound_notes = false;
2206 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2208 MidiModel::Notes& notes (_model->notes());
2209 _optimization_iterator = _events.begin();
2211 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2213 boost::shared_ptr<NoteType> note (*n);
2216 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2217 if ((cne = find_canvas_note (note)) != 0) {
2218 if (cne->selected()) {
2219 note_deselected (cne);
2221 note_selected (cne, true, false);
2229 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2232 clear_selection_except (ev);
2233 if (!_selection.empty()) {
2234 PublicEditor& editor (trackview.editor());
2235 editor.get_selection().add (this);
2241 if (!ev->selected()) {
2242 add_to_selection (ev);
2246 /* find end of latest note selected, select all between that and the start of "ev" */
2248 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2249 Evoral::MusicalTime latest = Evoral::MusicalTime();
2251 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2252 if ((*i)->note()->end_time() > latest) {
2253 latest = (*i)->note()->end_time();
2255 if ((*i)->note()->time() < earliest) {
2256 earliest = (*i)->note()->time();
2260 if (ev->note()->end_time() > latest) {
2261 latest = ev->note()->end_time();
2264 if (ev->note()->time() < earliest) {
2265 earliest = ev->note()->time();
2268 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2270 /* find notes entirely within OR spanning the earliest..latest range */
2272 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2273 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2274 add_to_selection (*i);
2282 MidiRegionView::note_deselected(NoteBase* ev)
2284 remove_from_selection (ev);
2288 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2290 PublicEditor& editor = trackview.editor();
2292 // Convert to local coordinates
2293 const framepos_t p = _region->position();
2294 const double y = midi_view()->y_position();
2295 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2296 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2297 const double y0 = max(0.0, gy0 - y);
2298 const double y1 = max(0.0, gy1 - y);
2300 // TODO: Make this faster by storing the last updated selection rect, and only
2301 // adjusting things that are in the area that appears/disappeared.
2302 // We probably need a tree to be able to find events in O(log(n)) time.
2304 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2305 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2306 // Rectangles intersect
2307 if (!(*i)->selected()) {
2308 add_to_selection (*i);
2310 } else if ((*i)->selected() && !extend) {
2311 // Rectangles do not intersect
2312 remove_from_selection (*i);
2316 typedef RouteTimeAxisView::AutomationTracks ATracks;
2317 typedef std::list<Selectable*> Selectables;
2319 /* Add control points to selection. */
2320 const ATracks& atracks = midi_view()->automation_tracks();
2321 Selectables selectables;
2322 editor.get_selection().clear_points();
2323 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2324 a->second->get_selectables(start, end, gy0, gy1, selectables);
2325 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2326 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2328 editor.get_selection().add(cp);
2331 a->second->set_selected_points(editor.get_selection().points);
2336 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2342 // TODO: Make this faster by storing the last updated selection rect, and only
2343 // adjusting things that are in the area that appears/disappeared.
2344 // We probably need a tree to be able to find events in O(log(n)) time.
2346 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2347 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2348 // within y- (note-) range
2349 if (!(*i)->selected()) {
2350 add_to_selection (*i);
2352 } else if ((*i)->selected() && !extend) {
2353 remove_from_selection (*i);
2359 MidiRegionView::remove_from_selection (NoteBase* ev)
2361 Selection::iterator i = _selection.find (ev);
2363 if (i != _selection.end()) {
2364 _selection.erase (i);
2365 if (_selection.empty() && _grabbed_keyboard) {
2367 Keyboard::magic_widget_drop_focus();
2368 _grabbed_keyboard = false;
2372 ev->set_selected (false);
2373 ev->hide_velocity ();
2375 if (_selection.empty()) {
2376 PublicEditor& editor (trackview.editor());
2377 editor.get_selection().remove (this);
2382 MidiRegionView::add_to_selection (NoteBase* ev)
2384 const bool selection_was_empty = _selection.empty();
2386 if (_selection.insert (ev).second) {
2387 ev->set_selected (true);
2388 start_playing_midi_note ((ev)->note());
2389 if (selection_was_empty && _entered) {
2390 // Grab keyboard for moving notes with arrow keys
2391 Keyboard::magic_widget_grab_focus();
2392 _grabbed_keyboard = true;
2396 if (selection_was_empty) {
2397 PublicEditor& editor (trackview.editor());
2398 editor.get_selection().add (this);
2403 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2405 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2406 PossibleChord to_play;
2407 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2409 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2410 if ((*i)->note()->time() < earliest) {
2411 earliest = (*i)->note()->time();
2415 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2416 if ((*i)->note()->time() == earliest) {
2417 to_play.push_back ((*i)->note());
2419 (*i)->move_event(dx, dy);
2422 if (dy && !_selection.empty() && !_no_sound_notes && ARDOUR_UI::config()->get_sound_midi_notes()) {
2424 if (to_play.size() > 1) {
2426 PossibleChord shifted;
2428 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2429 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2430 moved_note->set_note (moved_note->note() + cumulative_dy);
2431 shifted.push_back (moved_note);
2434 start_playing_midi_chord (shifted);
2436 } else if (!to_play.empty()) {
2438 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2439 moved_note->set_note (moved_note->note() + cumulative_dy);
2440 start_playing_midi_note (moved_note);
2446 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2448 uint8_t lowest_note_in_selection = 127;
2449 uint8_t highest_note_in_selection = 0;
2450 uint8_t highest_note_difference = 0;
2452 // find highest and lowest notes first
2454 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2455 uint8_t pitch = (*i)->note()->note();
2456 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2457 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2461 cerr << "dnote: " << (int) dnote << endl;
2462 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2463 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2464 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2465 << int(highest_note_in_selection) << endl;
2466 cerr << "selection size: " << _selection.size() << endl;
2467 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2470 // Make sure the note pitch does not exceed the MIDI standard range
2471 if (highest_note_in_selection + dnote > 127) {
2472 highest_note_difference = highest_note_in_selection - 127;
2475 start_note_diff_command (_("move notes"));
2477 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2479 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2480 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2486 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2488 uint8_t original_pitch = (*i)->note()->note();
2489 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2491 // keep notes in standard midi range
2492 clamp_to_0_127(new_pitch);
2494 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2495 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2497 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2502 // care about notes being moved beyond the upper/lower bounds on the canvas
2503 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2504 highest_note_in_selection > midi_stream_view()->highest_note()) {
2505 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2509 /** @param x Pixel relative to the region position.
2510 * @return Snapped frame relative to the region position.
2513 MidiRegionView::snap_pixel_to_sample(double x)
2515 PublicEditor& editor (trackview.editor());
2516 return snap_frame_to_frame (editor.pixel_to_sample (x));
2519 /** @param x Pixel relative to the region position.
2520 * @return Snapped pixel relative to the region position.
2523 MidiRegionView::snap_to_pixel(double x)
2525 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2529 MidiRegionView::get_position_pixels()
2531 framepos_t region_frame = get_position();
2532 return trackview.editor().sample_to_pixel(region_frame);
2536 MidiRegionView::get_end_position_pixels()
2538 framepos_t frame = get_position() + get_duration ();
2539 return trackview.editor().sample_to_pixel(frame);
2543 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2545 /* the time converter will return the frame corresponding to `beats'
2546 relative to the start of the source. The start of the source
2547 is an implied position given by region->position - region->start
2549 const framepos_t source_start = _region->position() - _region->start();
2550 return source_start + _source_relative_time_converter.to (beats);
2554 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2556 /* the `frames' argument needs to be converted into a frame count
2557 relative to the start of the source before being passed in to the
2560 const framepos_t source_start = _region->position() - _region->start();
2561 return _source_relative_time_converter.from (frames - source_start);
2565 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2567 return _region_relative_time_converter.to(beats);
2571 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2573 return _region_relative_time_converter.from(frames);
2577 MidiRegionView::begin_resizing (bool /*at_front*/)
2579 _resize_data.clear();
2581 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2582 Note *note = dynamic_cast<Note*> (*i);
2584 // only insert CanvasNotes into the map
2586 NoteResizeData *resize_data = new NoteResizeData();
2587 resize_data->note = note;
2589 // create a new SimpleRect from the note which will be the resize preview
2590 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2591 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2593 // calculate the colors: get the color settings
2594 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2595 ARDOUR_UI::config()->color ("midi note selected"),
2598 // make the resize preview notes more transparent and bright
2599 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2601 // calculate color based on note velocity
2602 resize_rect->set_fill_color (UINT_INTERPOLATE(
2603 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2607 resize_rect->set_outline_color (NoteBase::calculate_outline (
2608 ARDOUR_UI::config()->color ("midi note selected")));
2610 resize_data->resize_rect = resize_rect;
2611 _resize_data.push_back(resize_data);
2616 /** Update resizing notes while user drags.
2617 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2618 * @param at_front which end of the note (true == note on, false == note off)
2619 * @param delta_x change in mouse position since the start of the drag
2620 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2621 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2622 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2623 * as the \a primary note.
2626 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2628 bool cursor_set = false;
2630 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2631 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2632 Note* canvas_note = (*i)->note;
2637 current_x = canvas_note->x0() + delta_x;
2639 current_x = primary->x0() + delta_x;
2643 current_x = canvas_note->x1() + delta_x;
2645 current_x = primary->x1() + delta_x;
2649 if (current_x < 0) {
2650 // This works even with snapping because RegionView::snap_frame_to_frame()
2651 // snaps forward if the snapped sample is before the beginning of the region
2654 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2655 current_x = trackview.editor().sample_to_pixel(_region->length());
2659 resize_rect->set_x0 (snap_to_pixel(current_x));
2660 resize_rect->set_x1 (canvas_note->x1());
2662 resize_rect->set_x1 (snap_to_pixel(current_x));
2663 resize_rect->set_x0 (canvas_note->x0());
2667 const double snapped_x = snap_pixel_to_sample (current_x);
2668 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2669 Evoral::MusicalTime len = Evoral::MusicalTime();
2672 if (beats < canvas_note->note()->end_time()) {
2673 len = canvas_note->note()->time() - beats;
2674 len += canvas_note->note()->length();
2677 if (beats >= canvas_note->note()->time()) {
2678 len = beats - canvas_note->note()->time();
2683 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2684 show_verbose_cursor (buf, 0, 0);
2693 /** Finish resizing notes when the user releases the mouse button.
2694 * Parameters the same as for \a update_resizing().
2697 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2699 start_note_diff_command (_("resize notes"));
2701 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2702 Note* canvas_note = (*i)->note;
2703 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2705 /* Get the new x position for this resize, which is in pixels relative
2706 * to the region position.
2713 current_x = canvas_note->x0() + delta_x;
2715 current_x = primary->x0() + delta_x;
2719 current_x = canvas_note->x1() + delta_x;
2721 current_x = primary->x1() + delta_x;
2725 if (current_x < 0) {
2728 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2729 current_x = trackview.editor().sample_to_pixel(_region->length());
2732 /* Convert that to a frame within the source */
2733 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2735 /* and then to beats */
2736 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2738 if (at_front && x_beats < canvas_note->note()->end_time()) {
2739 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2741 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2742 len += canvas_note->note()->length();
2745 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2750 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2753 /* XXX convert to beats */
2754 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2762 _resize_data.clear();
2767 MidiRegionView::abort_resizing ()
2769 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2770 delete (*i)->resize_rect;
2774 _resize_data.clear ();
2778 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2780 uint8_t new_velocity;
2783 new_velocity = event->note()->velocity() + velocity;
2784 clamp_to_0_127(new_velocity);
2786 new_velocity = velocity;
2789 event->set_selected (event->selected()); // change color
2791 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2795 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2800 new_note = event->note()->note() + note;
2805 clamp_to_0_127 (new_note);
2806 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2810 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2812 bool change_start = false;
2813 bool change_length = false;
2814 Evoral::MusicalTime new_start;
2815 Evoral::MusicalTime new_length;
2817 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2819 front_delta: if positive - move the start of the note later in time (shortening it)
2820 if negative - move the start of the note earlier in time (lengthening it)
2822 end_delta: if positive - move the end of the note later in time (lengthening it)
2823 if negative - move the end of the note earlier in time (shortening it)
2826 if (!!front_delta) {
2827 if (front_delta < 0) {
2829 if (event->note()->time() < -front_delta) {
2830 new_start = Evoral::MusicalTime();
2832 new_start = event->note()->time() + front_delta; // moves earlier
2835 /* start moved toward zero, so move the end point out to where it used to be.
2836 Note that front_delta is negative, so this increases the length.
2839 new_length = event->note()->length() - front_delta;
2840 change_start = true;
2841 change_length = true;
2845 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2847 if (new_pos < event->note()->end_time()) {
2848 new_start = event->note()->time() + front_delta;
2849 /* start moved toward the end, so move the end point back to where it used to be */
2850 new_length = event->note()->length() - front_delta;
2851 change_start = true;
2852 change_length = true;
2859 bool can_change = true;
2860 if (end_delta < 0) {
2861 if (event->note()->length() < -end_delta) {
2867 new_length = event->note()->length() + end_delta;
2868 change_length = true;
2873 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2876 if (change_length) {
2877 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2882 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2884 uint8_t new_channel;
2888 if (event->note()->channel() < -chn) {
2891 new_channel = event->note()->channel() + chn;
2894 new_channel = event->note()->channel() + chn;
2897 new_channel = (uint8_t) chn;
2900 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2904 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2906 Evoral::MusicalTime new_time;
2910 if (event->note()->time() < -delta) {
2911 new_time = Evoral::MusicalTime();
2913 new_time = event->note()->time() + delta;
2916 new_time = event->note()->time() + delta;
2922 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2926 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2928 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2932 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2937 if (_selection.empty()) {
2952 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2953 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2959 start_note_diff_command (_("change velocities"));
2961 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2962 Selection::iterator next = i;
2966 if (i == _selection.begin()) {
2967 change_note_velocity (*i, delta, true);
2968 value = (*i)->note()->velocity() + delta;
2970 change_note_velocity (*i, value, false);
2974 change_note_velocity (*i, delta, true);
2983 if (!_selection.empty()) {
2985 snprintf (buf, sizeof (buf), "Vel %d",
2986 (int) (*_selection.begin())->note()->velocity());
2987 show_verbose_cursor (buf, 10, 10);
2993 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2995 if (_selection.empty()) {
3012 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3014 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3018 if ((int8_t) (*i)->note()->note() + delta > 127) {
3025 start_note_diff_command (_("transpose"));
3027 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3028 Selection::iterator next = i;
3030 change_note_note (*i, delta, true);
3038 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3042 delta = Evoral::MusicalTime(1.0/128.0);
3044 /* grab the current grid distance */
3045 delta = get_grid_beats(_region->position());
3053 start_note_diff_command (_("change note lengths"));
3055 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3056 Selection::iterator next = i;
3059 /* note the negation of the delta for start */
3062 (start ? -delta : Evoral::MusicalTime()),
3063 (end ? delta : Evoral::MusicalTime()));
3072 MidiRegionView::nudge_notes (bool forward, bool fine)
3074 if (_selection.empty()) {
3078 /* pick a note as the point along the timeline to get the nudge distance.
3079 its not necessarily the earliest note, so we may want to pull the notes out
3080 into a vector and sort before using the first one.
3083 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3084 Evoral::MusicalTime delta;
3088 /* non-fine, move by 1 bar regardless of snap */
3089 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3091 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3093 /* grid is off - use nudge distance */
3096 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3097 delta = region_frames_to_region_beats (fabs ((double)distance));
3103 framepos_t next_pos = ref_point;
3106 if (max_framepos - 1 < next_pos) {
3110 if (next_pos == 0) {
3116 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3117 const framecnt_t distance = ref_point - next_pos;
3118 delta = region_frames_to_region_beats (fabs ((double)distance));
3129 start_note_diff_command (_("nudge"));
3131 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3132 Selection::iterator next = i;
3134 change_note_time (*i, delta, true);
3142 MidiRegionView::change_channel(uint8_t channel)
3144 start_note_diff_command(_("change channel"));
3145 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3146 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3154 MidiRegionView::note_entered(NoteBase* ev)
3156 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3158 if (_mouse_state == SelectTouchDragging) {
3159 note_selected (ev, true);
3160 } else if (editor->current_mouse_mode() == MouseContent) {
3161 show_verbose_cursor (ev->note ());
3162 } else if (editor->current_mouse_mode() == MouseDraw) {
3163 show_verbose_cursor (ev->note ());
3168 MidiRegionView::note_left (NoteBase*)
3170 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3172 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3173 (*i)->hide_velocity ();
3176 editor->verbose_cursor()->hide ();
3180 MidiRegionView::patch_entered (PatchChange* p)
3183 /* XXX should get patch name if we can */
3184 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3185 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3186 << _("Channel ") << ((int) p->patch()->channel() + 1);
3187 show_verbose_cursor (s.str(), 10, 20);
3188 p->item().grab_focus();
3192 MidiRegionView::patch_left (PatchChange *)
3194 trackview.editor().verbose_cursor()->hide ();
3195 /* focus will transfer back via the enter-notify event sent to this
3201 MidiRegionView::sysex_entered (SysEx* p)
3205 // need a way to extract text from p->_flag->_text
3207 // show_verbose_cursor (s.str(), 10, 20);
3208 p->item().grab_focus();
3212 MidiRegionView::sysex_left (SysEx *)
3214 trackview.editor().verbose_cursor()->hide ();
3215 /* focus will transfer back via the enter-notify event sent to this
3221 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3223 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3224 Editing::MouseMode mm = editor->current_mouse_mode();
3225 bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3227 Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3228 if (can_set_cursor && ctx) {
3229 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3230 ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3231 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3232 ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3234 ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3240 MidiRegionView::get_fill_color() const
3242 const std::string mod_name = (_dragging ? "dragging region" :
3243 trackview.editor().internal_editing() ? "editable region" :
3246 return ARDOUR_UI::config()->color_mod ("selected region base", mod_name);
3247 } else if (high_enough_for_name || !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3248 return ARDOUR_UI::config()->color_mod ("midi frame base", mod_name);
3250 return ARDOUR_UI::config()->color_mod (fill_color, mod_name);
3254 MidiRegionView::midi_channel_mode_changed ()
3256 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3257 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3258 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3260 if (mode == ForceChannel) {
3261 mask = 0xFFFF; // Show all notes as active (below)
3264 // Update notes for selection
3265 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3266 (*i)->on_channel_selection_change (mask);
3269 _patch_changes.clear ();
3270 display_patch_changes ();
3274 MidiRegionView::instrument_settings_changed ()
3280 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3282 if (_selection.empty()) {
3286 PublicEditor& editor (trackview.editor());
3290 /* XXX what to do ? */
3294 editor.get_cut_buffer().add (selection_as_cut_buffer());
3302 start_note_diff_command();
3304 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3311 note_diff_remove_note (*i);
3321 MidiRegionView::selection_as_cut_buffer () const
3325 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3326 NoteType* n = (*i)->note().get();
3327 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3330 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3336 /** This method handles undo */
3338 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3340 // Paste notes, if available
3341 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3342 if (m != selection.midi_notes.end()) {
3343 ctx.counts.increase_n_notes();
3344 paste_internal(pos, ctx.count, ctx.times, **m);
3347 // Paste control points to automation children, if available
3348 typedef RouteTimeAxisView::AutomationTracks ATracks;
3349 const ATracks& atracks = midi_view()->automation_tracks();
3350 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3351 a->second->paste(pos, selection, ctx);
3357 /** This method handles undo */
3359 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3365 start_note_diff_command (_("paste"));
3367 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3368 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3369 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3370 const Evoral::MusicalTime duration = last_time - first_time;
3371 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3372 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3373 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3374 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3376 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3379 duration, pos, _region->position(),
3384 for (int n = 0; n < (int) times; ++n) {
3386 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3388 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3389 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3391 /* make all newly added notes selected */
3393 note_diff_add_note (copied_note, true);
3394 end_point = copied_note->end_time();
3398 /* if we pasted past the current end of the region, extend the region */
3400 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3401 framepos_t region_end = _region->position() + _region->length() - 1;
3403 if (end_frame > region_end) {
3405 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3407 _region->clear_changes ();
3408 _region->set_length (end_frame - _region->position());
3409 trackview.session()->add_command (new StatefulDiffCommand (_region));
3415 struct EventNoteTimeEarlyFirstComparator {
3416 bool operator() (NoteBase* a, NoteBase* b) {
3417 return a->note()->time() < b->note()->time();
3422 MidiRegionView::time_sort_events ()
3424 if (!_sort_needed) {
3428 EventNoteTimeEarlyFirstComparator cmp;
3431 _sort_needed = false;
3435 MidiRegionView::goto_next_note (bool add_to_selection)
3437 bool use_next = false;
3439 if (_events.back()->selected()) {
3443 time_sort_events ();
3445 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3446 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3448 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3449 if ((*i)->selected()) {
3452 } else if (use_next) {
3453 if (channel_mask & (1 << (*i)->note()->channel())) {
3454 if (!add_to_selection) {
3457 note_selected (*i, true, false);
3464 /* use the first one */
3466 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3467 unique_select (_events.front());
3472 MidiRegionView::goto_previous_note (bool add_to_selection)
3474 bool use_next = false;
3476 if (_events.front()->selected()) {
3480 time_sort_events ();
3482 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3483 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3485 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3486 if ((*i)->selected()) {
3489 } else if (use_next) {
3490 if (channel_mask & (1 << (*i)->note()->channel())) {
3491 if (!add_to_selection) {
3494 note_selected (*i, true, false);
3501 /* use the last one */
3503 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3504 unique_select (*(_events.rbegin()));
3509 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3511 bool had_selected = false;
3513 time_sort_events ();
3515 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3516 if ((*i)->selected()) {
3517 selected.insert ((*i)->note());
3518 had_selected = true;
3522 if (allow_all_if_none_selected && !had_selected) {
3523 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3524 selected.insert ((*i)->note());
3530 MidiRegionView::update_ghost_note (double x, double y)
3532 x = std::max(0.0, x);
3534 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3539 _note_group->canvas_to_item (x, y);
3541 PublicEditor& editor = trackview.editor ();
3543 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3544 framecnt_t grid_frames;
3545 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3547 /* calculate time in beats relative to start of source */
3548 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3549 const Evoral::MusicalTime time = std::max(
3550 Evoral::MusicalTime(),
3551 absolute_frames_to_source_beats (f + _region->position ()));
3553 _ghost_note->note()->set_time (time);
3554 _ghost_note->note()->set_length (length);
3555 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3556 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3557 _ghost_note->note()->set_velocity (get_velocity_for_add (time));
3559 /* the ghost note does not appear in ghost regions, so pass false in here */
3560 update_note (_ghost_note, false);
3562 show_verbose_cursor (_ghost_note->note ());
3566 MidiRegionView::create_ghost_note (double x, double y)
3568 remove_ghost_note ();
3570 boost::shared_ptr<NoteType> g (new NoteType);
3571 if (midi_view()->note_mode() == Sustained) {
3572 _ghost_note = new Note (*this, _note_group, g);
3574 _ghost_note = new Hit (*this, _note_group, 10, g);
3576 _ghost_note->set_ignore_events (true);
3577 _ghost_note->set_outline_color (0x000000aa);
3578 update_ghost_note (x, y);
3579 _ghost_note->show ();
3581 show_verbose_cursor (_ghost_note->note ());
3585 MidiRegionView::remove_ghost_note ()
3592 MidiRegionView::snap_changed ()
3598 create_ghost_note (_last_ghost_x, _last_ghost_y);
3602 MidiRegionView::drop_down_keys ()
3604 _mouse_state = None;
3608 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3610 /* XXX: This is dead code. What was it for? */
3612 double note = midi_stream_view()->y_to_note(y);
3614 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3616 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3618 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3619 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3620 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3621 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3626 bool add_mrv_selection = false;
3628 if (_selection.empty()) {
3629 add_mrv_selection = true;
3632 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3633 if (_selection.insert (*i).second) {
3634 (*i)->set_selected (true);
3638 if (add_mrv_selection) {
3639 PublicEditor& editor (trackview.editor());
3640 editor.get_selection().add (this);
3645 MidiRegionView::color_handler ()
3647 RegionView::color_handler ();
3649 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3650 (*i)->set_selected ((*i)->selected()); // will change color
3653 /* XXX probably more to do here */
3657 MidiRegionView::enable_display (bool yn)
3659 RegionView::enable_display (yn);
3666 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3668 if (_step_edit_cursor == 0) {
3669 ArdourCanvas::Item* const group = get_canvas_group();
3671 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3672 _step_edit_cursor->set_y0 (0);
3673 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3674 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3675 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3678 move_step_edit_cursor (pos);
3679 _step_edit_cursor->show ();
3683 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3685 _step_edit_cursor_position = pos;
3687 if (_step_edit_cursor) {
3688 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3689 _step_edit_cursor->set_x0 (pixel);
3690 set_step_edit_cursor_width (_step_edit_cursor_width);
3695 MidiRegionView::hide_step_edit_cursor ()
3697 if (_step_edit_cursor) {
3698 _step_edit_cursor->hide ();
3703 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3705 _step_edit_cursor_width = beats;
3707 if (_step_edit_cursor) {
3708 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3712 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3713 * @param w Source that the data will end up in.
3716 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3718 if (!_active_notes) {
3719 /* we aren't actively being recorded to */
3723 boost::shared_ptr<MidiSource> src = w.lock ();
3724 if (!src || src != midi_region()->midi_source()) {
3725 /* recorded data was not destined for our source */
3729 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3731 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3733 framepos_t back = max_framepos;
3735 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3736 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3738 if (ev.is_channel_event()) {
3739 if (get_channel_mode() == FilterChannels) {
3740 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3746 /* convert from session frames to source beats */
3747 Evoral::MusicalTime const time_beats = _source_relative_time_converter.from(ev.time());
3749 if (ev.type() == MIDI_CMD_NOTE_ON) {
3750 boost::shared_ptr<NoteType> note (
3751 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3753 add_note (note, true);
3755 /* fix up our note range */
3756 if (ev.note() < _current_range_min) {
3757 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3758 } else if (ev.note() > _current_range_max) {
3759 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3762 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3763 resolve_note (ev.note (), time_beats);
3769 midi_stream_view()->check_record_layers (region(), back);
3773 MidiRegionView::trim_front_starting ()
3775 /* Reparent the note group to the region view's parent, so that it doesn't change
3776 when the region view is trimmed.
3778 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3779 _temporary_note_group->move (group->position ());
3780 _note_group->reparent (_temporary_note_group);
3784 MidiRegionView::trim_front_ending ()
3786 _note_group->reparent (group);
3787 delete _temporary_note_group;
3788 _temporary_note_group = 0;
3790 if (_region->start() < 0) {
3791 /* Trim drag made start time -ve; fix this */
3792 midi_region()->fix_negative_start ();
3797 MidiRegionView::edit_patch_change (PatchChange* pc)
3799 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3801 int response = d.run();
3804 case Gtk::RESPONSE_ACCEPT:
3806 case Gtk::RESPONSE_REJECT:
3807 delete_patch_change (pc);
3813 change_patch_change (pc->patch(), d.patch ());
3817 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3820 // sysyex object doesn't have a pointer to a sysex event
3821 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3822 // c->remove (sysex->sysex());
3823 // _model->apply_command (*trackview.session(), c);
3825 //_sys_exes.clear ();
3826 // display_sysexes();
3830 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3832 using namespace MIDI::Name;
3836 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3838 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3840 MIDI::Name::PatchPrimaryKey patch_key;
3841 get_patch_key_at(n->time(), n->channel(), patch_key);
3842 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3845 patch_key.program(),
3851 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3853 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3854 (int) n->channel() + 1,
3855 (int) n->velocity());
3857 show_verbose_cursor(buf, 10, 20);
3861 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3863 trackview.editor().verbose_cursor()->set (text);
3864 trackview.editor().verbose_cursor()->show ();
3865 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3869 MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
3871 if (_model->notes().empty()) {
3872 return 0x40; // No notes, use default
3875 MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
3876 if (m == _model->notes().begin()) {
3877 // Before the start, use the velocity of the first note
3878 return (*m)->velocity();
3879 } else if (m == _model->notes().end()) {
3880 // Past the end, use the velocity of the last note
3882 return (*m)->velocity();
3885 // Interpolate velocity of surrounding notes
3886 MidiModel::Notes::const_iterator n = m;
3889 const double frac = ((time - (*n)->time()).to_double() /
3890 ((*m)->time() - (*n)->time()).to_double());
3892 return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
3895 /** @param p A session framepos.
3896 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3897 * @return p snapped to the grid subdivision underneath it.
3900 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3902 PublicEditor& editor = trackview.editor ();
3904 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3906 grid_frames = region_beats_to_region_frames (grid_beats);
3908 /* Hack so that we always snap to the note that we are over, instead of snapping
3909 to the next one if we're more than halfway through the one we're over.
3911 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3912 p -= grid_frames / 2;
3915 return snap_frame_to_frame (p);
3918 /** Called when the selection has been cleared in any MidiRegionView.
3919 * @param rv MidiRegionView that the selection was cleared in.
3922 MidiRegionView::selection_cleared (MidiRegionView* rv)
3928 /* Clear our selection in sympathy; but don't signal the fact */
3929 clear_selection (false);
3933 MidiRegionView::note_button_release ()
3935 _note_player.reset();
3939 MidiRegionView::get_channel_mode () const
3941 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3942 return rtav->midi_track()->get_playback_channel_mode();
3946 MidiRegionView::get_selected_channels () const
3948 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3949 return rtav->midi_track()->get_playback_channel_mask();
3954 MidiRegionView::get_grid_beats(framepos_t pos) const
3956 PublicEditor& editor = trackview.editor();
3957 bool success = false;
3958 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3960 beats = Evoral::MusicalTime(1);