2 Copyright (C) 2001-2011 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "gtkmm2ext/gtk_ui.h"
28 #include <sigc++/signal.h>
30 #include "midi++/midnam_patch.h"
32 #include "pbd/memento_command.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "ardour/midi_model.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIEvent.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "canvas/debug.h"
48 #include "canvas/text.h"
50 #include "automation_region_view.h"
51 #include "automation_time_axis.h"
52 #include "control_point.h"
55 #include "editor_drag.h"
56 #include "ghostregion.h"
57 #include "gui_thread.h"
58 #include "item_counts.h"
60 #include "midi_channel_dialog.h"
61 #include "midi_cut_buffer.h"
62 #include "midi_list_editor.h"
63 #include "midi_region_view.h"
64 #include "midi_streamview.h"
65 #include "midi_time_axis.h"
66 #include "midi_util.h"
67 #include "midi_velocity_dialog.h"
68 #include "mouse_cursors.h"
69 #include "note_player.h"
70 #include "paste_context.h"
71 #include "public_editor.h"
72 #include "route_time_axis.h"
73 #include "rgb_macros.h"
74 #include "selection.h"
75 #include "streamview.h"
76 #include "patch_change_dialog.h"
77 #include "verbose_cursor.h"
78 #include "ardour_ui.h"
81 #include "patch_change.h"
86 using namespace ARDOUR;
88 using namespace Editing;
90 using Gtkmm2ext::Keyboard;
92 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
94 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
96 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
97 RouteTimeAxisView& tv,
98 boost::shared_ptr<MidiRegion> r,
100 uint32_t basic_color)
101 : RegionView (parent, tv, r, spu, basic_color)
102 , _current_range_min(0)
103 , _current_range_max(0)
104 , _region_relative_time_converter(r->session().tempo_map(), r->position())
105 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
107 , _note_group (new ArdourCanvas::Container (group))
108 , _note_diff_command (0)
110 , _step_edit_cursor (0)
111 , _step_edit_cursor_width (1.0)
112 , _step_edit_cursor_position (0.0)
113 , _channel_selection_scoped_note (0)
114 , _temporary_note_group (0)
117 , _sort_needed (true)
118 , _optimization_iterator (_events.end())
120 , _no_sound_notes (false)
123 , _grabbed_keyboard (false)
125 , pre_enter_cursor (0)
126 , pre_press_cursor (0)
127 , pre_note_enter_cursor (0)
130 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
131 _note_group->raise_to_top();
132 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
134 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
135 connect_to_diskstream ();
137 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
139 PublicEditor& editor (trackview.editor());
140 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
143 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
144 RouteTimeAxisView& tv,
145 boost::shared_ptr<MidiRegion> r,
147 uint32_t basic_color,
149 TimeAxisViewItem::Visibility visibility)
150 : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
151 , _current_range_min(0)
152 , _current_range_max(0)
153 , _region_relative_time_converter(r->session().tempo_map(), r->position())
154 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
156 , _note_group (new ArdourCanvas::Container (parent))
157 , _note_diff_command (0)
159 , _step_edit_cursor (0)
160 , _step_edit_cursor_width (1.0)
161 , _step_edit_cursor_position (0.0)
162 , _channel_selection_scoped_note (0)
163 , _temporary_note_group (0)
166 , _sort_needed (true)
167 , _optimization_iterator (_events.end())
169 , _no_sound_notes (false)
172 , _grabbed_keyboard (false)
174 , pre_enter_cursor (0)
175 , pre_press_cursor (0)
176 , pre_note_enter_cursor (0)
179 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
180 _note_group->raise_to_top();
182 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
184 connect_to_diskstream ();
186 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
188 PublicEditor& editor (trackview.editor());
189 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
193 MidiRegionView::parameter_changed (std::string const & p)
195 if (p == "display-first-midi-bank-as-zero") {
196 if (_enable_display) {
202 MidiRegionView::MidiRegionView (const MidiRegionView& other)
203 : sigc::trackable(other)
205 , _current_range_min(0)
206 , _current_range_max(0)
207 , _region_relative_time_converter(other.region_relative_time_converter())
208 , _source_relative_time_converter(other.source_relative_time_converter())
210 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
211 , _note_diff_command (0)
213 , _step_edit_cursor (0)
214 , _step_edit_cursor_width (1.0)
215 , _step_edit_cursor_position (0.0)
216 , _channel_selection_scoped_note (0)
217 , _temporary_note_group (0)
220 , _sort_needed (true)
221 , _optimization_iterator (_events.end())
223 , _no_sound_notes (false)
226 , _grabbed_keyboard (false)
228 , pre_enter_cursor (0)
229 , pre_press_cursor (0)
230 , pre_note_enter_cursor (0)
236 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
237 : RegionView (other, boost::shared_ptr<Region> (region))
238 , _current_range_min(0)
239 , _current_range_max(0)
240 , _region_relative_time_converter(other.region_relative_time_converter())
241 , _source_relative_time_converter(other.source_relative_time_converter())
243 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
244 , _note_diff_command (0)
246 , _step_edit_cursor (0)
247 , _step_edit_cursor_width (1.0)
248 , _step_edit_cursor_position (0.0)
249 , _channel_selection_scoped_note (0)
250 , _temporary_note_group (0)
253 , _sort_needed (true)
254 , _optimization_iterator (_events.end())
256 , _no_sound_notes (false)
259 , _grabbed_keyboard (false)
261 , pre_enter_cursor (0)
262 , pre_press_cursor (0)
263 , pre_note_enter_cursor (0)
270 MidiRegionView::init (bool wfd)
272 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
274 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
275 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
279 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
280 midi_region()->midi_source(0)->load_model(lm);
283 _model = midi_region()->midi_source(0)->model();
284 _enable_display = false;
285 _fill_color_name = "midi frame base";
287 RegionView::init (false);
289 set_height (trackview.current_height());
292 region_sync_changed ();
293 region_resized (ARDOUR::bounds_change);
298 _enable_display = true;
301 display_model (_model);
305 reset_width_dependent_items (_pixel_width);
307 group->raise_to_top();
309 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
310 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
313 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
314 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
316 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
317 boost::bind (&MidiRegionView::snap_changed, this),
320 trackview.editor().MouseModeChanged.connect(_mouse_mode_connection, invalidator (*this),
321 boost::bind (&MidiRegionView::mouse_mode_changed, this),
324 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
325 connect_to_diskstream ();
327 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
329 PublicEditor& editor (trackview.editor());
330 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
334 MidiRegionView::instrument_info () const
336 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
337 return route_ui->route()->instrument_info();
340 const boost::shared_ptr<ARDOUR::MidiRegion>
341 MidiRegionView::midi_region() const
343 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
347 MidiRegionView::connect_to_diskstream ()
349 midi_view()->midi_track()->DataRecorded.connect(
350 *this, invalidator(*this),
351 boost::bind (&MidiRegionView::data_recorded, this, _1),
356 MidiRegionView::canvas_group_event(GdkEvent* ev)
358 if (in_destructor || _recregion) {
362 if (!trackview.editor().internal_editing()) {
363 // not in internal edit mode, so just act like a normal region
364 return RegionView::canvas_group_event (ev);
370 case GDK_ENTER_NOTIFY:
371 _last_event_x = ev->crossing.x;
372 _last_event_y = ev->crossing.y;
373 enter_notify(&ev->crossing);
374 // set entered_regionview (among other things)
375 return RegionView::canvas_group_event (ev);
377 case GDK_LEAVE_NOTIFY:
378 _last_event_x = ev->crossing.x;
379 _last_event_y = ev->crossing.y;
380 leave_notify(&ev->crossing);
381 // reset entered_regionview (among other things)
382 return RegionView::canvas_group_event (ev);
385 if (scroll (&ev->scroll)) {
391 return key_press (&ev->key);
393 case GDK_KEY_RELEASE:
394 return key_release (&ev->key);
396 case GDK_BUTTON_PRESS:
397 return button_press (&ev->button);
399 case GDK_BUTTON_RELEASE:
400 r = button_release (&ev->button);
405 case GDK_MOTION_NOTIFY:
406 _last_event_x = ev->motion.x;
407 _last_event_y = ev->motion.y;
408 return motion (&ev->motion);
414 return RegionView::canvas_group_event (ev);
418 MidiRegionView::enter_notify (GdkEventCrossing* ev)
427 MidiRegionView::leave_notify (GdkEventCrossing*)
429 _mouse_mode_connection.disconnect ();
438 MidiRegionView::mouse_mode_changed ()
440 // Adjust frame colour (become more transparent for internal tools)
444 if (trackview.editor().internal_editing()) {
445 // Switched in to internal editing mode while entered
448 // Switched out of internal editing mode while entered
455 MidiRegionView::enter_internal()
457 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
458 // Show ghost note under pencil
459 create_ghost_note(_last_event_x, _last_event_y);
462 if (!_selection.empty()) {
463 // Grab keyboard for moving selected notes with arrow keys
464 Keyboard::magic_widget_grab_focus();
465 _grabbed_keyboard = true;
470 MidiRegionView::leave_internal()
472 trackview.editor().verbose_cursor()->hide ();
473 remove_ghost_note ();
475 if (_grabbed_keyboard) {
476 Keyboard::magic_widget_drop_focus();
477 _grabbed_keyboard = false;
482 MidiRegionView::button_press (GdkEventButton* ev)
484 if (ev->button != 1) {
488 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
489 MouseMode m = editor->current_mouse_mode();
491 if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
492 pre_press_cursor = editor->get_canvas_cursor ();
493 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
496 if (_mouse_state != SelectTouchDragging) {
498 _pressed_button = ev->button;
499 _mouse_state = Pressed;
504 _pressed_button = ev->button;
510 MidiRegionView::button_release (GdkEventButton* ev)
512 double event_x, event_y;
514 if (ev->button != 1) {
521 group->canvas_to_item (event_x, event_y);
524 PublicEditor& editor = trackview.editor ();
526 if (pre_press_cursor) {
527 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
528 pre_press_cursor = 0;
531 switch (_mouse_state) {
532 case Pressed: // Clicked
534 switch (editor.current_mouse_mode()) {
536 /* no motion occured - simple click */
545 if (Keyboard::is_insert_note_event(ev)) {
547 double event_x, event_y;
551 group->canvas_to_item (event_x, event_y);
553 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
555 /* Shorten the length by 1 tick so that we can add a new note at the next
556 grid snap without it overlapping this one.
558 beats -= Evoral::MusicalTime::tick();
560 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
567 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
569 /* Shorten the length by 1 tick so that we can add a new note at the next
570 grid snap without it overlapping this one.
572 beats -= Evoral::MusicalTime::tick();
574 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
585 case SelectRectDragging:
587 editor.drags()->end_grab ((GdkEvent *) ev);
589 create_ghost_note (ev->x, ev->y);
601 MidiRegionView::motion (GdkEventMotion* ev)
603 PublicEditor& editor = trackview.editor ();
605 if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
606 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
607 _mouse_state != AddDragging) {
609 create_ghost_note (ev->x, ev->y);
611 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
612 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
614 update_ghost_note (ev->x, ev->y);
616 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
618 remove_ghost_note ();
619 editor.verbose_cursor()->hide ();
621 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
623 update_ghost_note (ev->x, ev->y);
626 /* any motion immediately hides velocity text that may have been visible */
628 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
629 (*i)->hide_velocity ();
632 switch (_mouse_state) {
635 if (_pressed_button == 1) {
637 MouseMode m = editor.current_mouse_mode();
639 if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
640 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
641 _mouse_state = AddDragging;
642 remove_ghost_note ();
643 editor.verbose_cursor()->hide ();
645 } else if (m == MouseContent) {
646 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
647 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
650 _mouse_state = SelectRectDragging;
652 } else if (m == MouseRange) {
653 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
654 _mouse_state = SelectVerticalDragging;
661 case SelectRectDragging:
662 case SelectVerticalDragging:
664 editor.drags()->motion_handler ((GdkEvent *) ev, false);
667 case SelectTouchDragging:
675 /* we may be dragging some non-note object (eg. patch-change, sysex)
678 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
683 MidiRegionView::scroll (GdkEventScroll* ev)
685 if (_selection.empty()) {
689 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
690 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
691 it still works for zoom.
696 trackview.editor().verbose_cursor()->hide ();
698 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
699 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
701 if (ev->direction == GDK_SCROLL_UP) {
702 change_velocities (true, fine, false, together);
703 } else if (ev->direction == GDK_SCROLL_DOWN) {
704 change_velocities (false, fine, false, together);
706 /* left, right: we don't use them */
714 MidiRegionView::key_press (GdkEventKey* ev)
716 /* since GTK bindings are generally activated on press, and since
717 detectable auto-repeat is the name of the game and only sends
718 repeated presses, carry out key actions at key press, not release.
721 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
723 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
724 _mouse_state = SelectTouchDragging;
727 } else if (ev->keyval == GDK_Escape && unmodified) {
731 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
733 bool start = (ev->keyval == GDK_comma);
734 bool end = (ev->keyval == GDK_period);
735 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
736 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
738 change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
742 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
744 if (_selection.empty()) {
751 } else if (ev->keyval == GDK_Tab) {
753 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
754 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
756 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
760 } else if (ev->keyval == GDK_ISO_Left_Tab) {
762 /* Shift-TAB generates ISO Left Tab, for some reason */
764 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
765 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
767 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
773 } else if (ev->keyval == GDK_Up) {
775 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
776 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
777 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
779 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
780 change_velocities (true, fine, allow_smush, together);
782 transpose (true, fine, allow_smush);
786 } else if (ev->keyval == GDK_Down) {
788 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
789 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
790 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
792 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
793 change_velocities (false, fine, allow_smush, together);
795 transpose (false, fine, allow_smush);
799 } else if (ev->keyval == GDK_Left) {
801 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
802 nudge_notes (false, fine);
805 } else if (ev->keyval == GDK_Right) {
807 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
808 nudge_notes (true, fine);
811 } else if (ev->keyval == GDK_c && unmodified) {
815 } else if (ev->keyval == GDK_v && unmodified) {
824 MidiRegionView::key_release (GdkEventKey* ev)
826 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
834 MidiRegionView::channel_edit ()
836 if (_selection.empty()) {
840 /* pick a note somewhat at random (since Selection is a set<>) to
841 * provide the "current" channel for the dialog.
844 uint8_t current_channel = (*_selection.begin())->note()->channel ();
845 MidiChannelDialog channel_dialog (current_channel);
846 int ret = channel_dialog.run ();
849 case Gtk::RESPONSE_OK:
855 uint8_t new_channel = channel_dialog.active_channel ();
857 start_note_diff_command (_("channel edit"));
859 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
860 Selection::iterator next = i;
862 change_note_channel (*i, new_channel);
870 MidiRegionView::velocity_edit ()
872 if (_selection.empty()) {
876 /* pick a note somewhat at random (since Selection is a set<>) to
877 * provide the "current" velocity for the dialog.
880 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
881 MidiVelocityDialog velocity_dialog (current_velocity);
882 int ret = velocity_dialog.run ();
885 case Gtk::RESPONSE_OK:
891 uint8_t new_velocity = velocity_dialog.velocity ();
893 start_note_diff_command (_("velocity edit"));
895 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
896 Selection::iterator next = i;
898 change_note_velocity (*i, new_velocity, false);
906 MidiRegionView::show_list_editor ()
909 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
911 _list_editor->present ();
914 /** Add a note to the model, and the view, at a canvas (click) coordinate.
915 * \param t time in frames relative to the position of the region
916 * \param y vertical position in pixels
917 * \param length duration of the note in beats
918 * \param snap_t true to snap t to the grid, otherwise false.
921 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
923 if (length < 2 * DBL_EPSILON) {
927 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
928 MidiStreamView* const view = mtv->midi_view();
930 const double note = view->y_to_note(y);
932 // Start of note in frames relative to region start
934 framecnt_t grid_frames;
935 t = snap_frame_to_grid_underneath (t, grid_frames);
938 const boost::shared_ptr<NoteType> new_note (
939 new NoteType (mtv->get_channel_for_add (),
940 region_frames_to_region_beats(t + _region->start()),
942 (uint8_t)note, 0x40));
944 if (_model->contains (new_note)) {
948 view->update_note_range(new_note->note());
950 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
952 _model->apply_command(*trackview.session(), cmd);
954 play_midi_note (new_note);
958 MidiRegionView::clear_events (bool with_selection_signal)
960 clear_selection (with_selection_signal);
963 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
964 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
969 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
974 _patch_changes.clear();
976 _optimization_iterator = _events.end();
980 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
984 content_connection.disconnect ();
985 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
989 if (_enable_display) {
995 MidiRegionView::start_note_diff_command (string name)
997 if (!_note_diff_command) {
998 _note_diff_command = _model->new_note_diff_command (name);
1003 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1005 if (_note_diff_command) {
1006 _note_diff_command->add (note);
1009 _marked_for_selection.insert(note);
1011 if (show_velocity) {
1012 _marked_for_velocity.insert(note);
1017 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1019 if (_note_diff_command && ev->note()) {
1020 _note_diff_command->remove(ev->note());
1025 MidiRegionView::note_diff_add_change (NoteBase* ev,
1026 MidiModel::NoteDiffCommand::Property property,
1029 if (_note_diff_command) {
1030 _note_diff_command->change (ev->note(), property, val);
1035 MidiRegionView::note_diff_add_change (NoteBase* ev,
1036 MidiModel::NoteDiffCommand::Property property,
1037 Evoral::MusicalTime val)
1039 if (_note_diff_command) {
1040 _note_diff_command->change (ev->note(), property, val);
1045 MidiRegionView::apply_diff (bool as_subcommand)
1049 if (!_note_diff_command) {
1053 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1054 // Mark all selected notes for selection when model reloads
1055 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1056 _marked_for_selection.insert((*i)->note());
1060 if (as_subcommand) {
1061 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1063 _model->apply_command (*trackview.session(), _note_diff_command);
1066 _note_diff_command = 0;
1067 midi_view()->midi_track()->playlist_modified();
1069 if (add_or_remove) {
1070 _marked_for_selection.clear();
1073 _marked_for_velocity.clear();
1077 MidiRegionView::abort_command()
1079 delete _note_diff_command;
1080 _note_diff_command = 0;
1085 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1087 if (_optimization_iterator != _events.end()) {
1088 ++_optimization_iterator;
1091 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1092 return *_optimization_iterator;
1095 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1096 if ((*_optimization_iterator)->note() == note) {
1097 return *_optimization_iterator;
1105 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1107 MidiModel::Notes notes;
1108 _model->get_notes (notes, op, val, chan_mask);
1110 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1111 NoteBase* cne = find_canvas_note (*n);
1119 MidiRegionView::redisplay_model()
1121 // Don't redisplay the model if we're currently recording and displaying that
1122 if (_active_notes) {
1130 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1131 (*i)->invalidate ();
1134 MidiModel::ReadLock lock(_model->read_lock());
1136 MidiModel::Notes& notes (_model->notes());
1137 _optimization_iterator = _events.begin();
1139 bool empty_when_starting = _events.empty();
1141 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1143 boost::shared_ptr<NoteType> note (*n);
1147 if (note_in_region_range (note, visible)) {
1149 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1156 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1158 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1170 add_note (note, visible);
1175 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1183 /* remove note items that are no longer valid */
1185 if (!empty_when_starting) {
1186 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1187 if (!(*i)->valid ()) {
1189 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1190 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1192 gr->remove_note (*i);
1197 i = _events.erase (i);
1205 _patch_changes.clear();
1209 display_patch_changes ();
1211 _marked_for_selection.clear ();
1212 _marked_for_velocity.clear ();
1214 /* we may have caused _events to contain things out of order (e.g. if a note
1215 moved earlier or later). we don't generally need them in time order, but
1216 make a note that a sort is required for those cases that require it.
1219 _sort_needed = true;
1223 MidiRegionView::display_patch_changes ()
1225 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1226 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1228 for (uint8_t i = 0; i < 16; ++i) {
1229 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1233 /** @param active_channel true to display patch changes fully, false to display
1234 * them `greyed-out' (as on an inactive channel)
1237 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1239 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1241 if ((*i)->channel() != channel) {
1245 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1246 add_canvas_patch_change (*i, patch_name, active_channel);
1251 MidiRegionView::display_sysexes()
1253 bool have_periodic_system_messages = false;
1254 bool display_periodic_messages = true;
1256 if (!Config->get_never_display_periodic_midi()) {
1258 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1259 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1260 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1263 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1264 have_periodic_system_messages = true;
1270 if (have_periodic_system_messages) {
1271 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1273 /* get an approximate value for the number of samples per video frame */
1275 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1277 /* if we are zoomed out beyond than the cutoff (i.e. more
1278 * frames per pixel than frames per 4 video frames), don't
1279 * show periodic sysex messages.
1282 if (zoom > (video_frame*4)) {
1283 display_periodic_messages = false;
1287 display_periodic_messages = false;
1290 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1292 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1293 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1295 Evoral::MusicalTime time = (*i)->time();
1298 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1299 if (!display_periodic_messages) {
1307 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1308 str << int((*i)->buffer()[b]);
1309 if (b != (*i)->size() -1) {
1313 string text = str.str();
1315 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1317 double height = midi_stream_view()->contents_height();
1319 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1320 // SysEx canvas object!!!
1322 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1323 new SysEx (*this, _note_group, text, height, x, 1.0));
1325 // Show unless message is beyond the region bounds
1326 if (time - _region->start() >= _region->length() || time < _region->start()) {
1332 _sys_exes.push_back(sysex);
1336 MidiRegionView::~MidiRegionView ()
1338 in_destructor = true;
1340 trackview.editor().verbose_cursor()->hide ();
1342 note_delete_connection.disconnect ();
1344 delete _list_editor;
1346 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1348 if (_active_notes) {
1352 _selection_cleared_connection.disconnect ();
1355 clear_events (false);
1358 delete _note_diff_command;
1359 delete _step_edit_cursor;
1360 delete _temporary_note_group;
1364 MidiRegionView::region_resized (const PropertyChange& what_changed)
1366 RegionView::region_resized(what_changed);
1368 if (what_changed.contains (ARDOUR::Properties::position)) {
1369 _region_relative_time_converter.set_origin_b(_region->position());
1370 set_duration(_region->length(), 0);
1371 if (_enable_display) {
1376 if (what_changed.contains (ARDOUR::Properties::start) ||
1377 what_changed.contains (ARDOUR::Properties::position)) {
1378 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1383 MidiRegionView::reset_width_dependent_items (double pixel_width)
1385 RegionView::reset_width_dependent_items(pixel_width);
1387 if (_enable_display) {
1391 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1392 if ((*x)->canvas_item()->width() >= _pixel_width) {
1399 move_step_edit_cursor (_step_edit_cursor_position);
1400 set_step_edit_cursor_width (_step_edit_cursor_width);
1404 MidiRegionView::set_height (double height)
1406 double old_height = _height;
1407 RegionView::set_height(height);
1409 apply_note_range (midi_stream_view()->lowest_note(),
1410 midi_stream_view()->highest_note(),
1411 height != old_height);
1414 name_text->raise_to_top();
1417 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1418 (*x)->set_height (midi_stream_view()->contents_height());
1421 if (_step_edit_cursor) {
1422 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1427 /** Apply the current note range from the stream view
1428 * by repositioning/hiding notes as necessary
1431 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1433 if (!_enable_display) {
1437 if (!force && _current_range_min == min && _current_range_max == max) {
1441 _current_range_min = min;
1442 _current_range_max = max;
1444 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1445 NoteBase* event = *i;
1446 boost::shared_ptr<NoteType> note (event->note());
1448 if (note->note() < _current_range_min ||
1449 note->note() > _current_range_max) {
1455 if (Note* cnote = dynamic_cast<Note*>(event)) {
1457 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1458 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1463 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1470 MidiRegionView::add_ghost (TimeAxisView& tv)
1474 double unit_position = _region->position () / samples_per_pixel;
1475 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1476 MidiGhostRegion* ghost;
1478 if (mtv && mtv->midi_view()) {
1479 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1480 to allow having midi notes on top of note lines and waveforms.
1482 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1484 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1487 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1488 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1489 ghost->add_note(note);
1493 ghost->set_height ();
1494 ghost->set_duration (_region->length() / samples_per_pixel);
1495 ghosts.push_back (ghost);
1497 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1503 /** Begin tracking note state for successive calls to add_event
1506 MidiRegionView::begin_write()
1508 if (_active_notes) {
1509 delete[] _active_notes;
1511 _active_notes = new Note*[128];
1512 for (unsigned i = 0; i < 128; ++i) {
1513 _active_notes[i] = 0;
1518 /** Destroy note state for add_event
1521 MidiRegionView::end_write()
1523 delete[] _active_notes;
1525 _marked_for_selection.clear();
1526 _marked_for_velocity.clear();
1530 /** Resolve an active MIDI note (while recording).
1533 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1535 if (midi_view()->note_mode() != Sustained) {
1539 if (_active_notes && _active_notes[note]) {
1541 /* XXX is end_time really region-centric? I think so, because
1542 this is a new region that we're recording, so source zero is
1543 the same as region zero
1545 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1547 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1548 _active_notes[note]->set_outline_all ();
1549 _active_notes[note] = 0;
1555 /** Extend active notes to rightmost edge of region (if length is changed)
1558 MidiRegionView::extend_active_notes()
1560 if (!_active_notes) {
1564 for (unsigned i=0; i < 128; ++i) {
1565 if (_active_notes[i]) {
1566 _active_notes[i]->set_x1(
1567 trackview.editor().sample_to_pixel(_region->position() + _region->length()));
1574 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1576 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1580 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1582 if (!route_ui || !route_ui->midi_track()) {
1586 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1590 /* NotePlayer deletes itself */
1594 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1596 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1600 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1602 if (!route_ui || !route_ui->midi_track()) {
1606 delete _note_player;
1607 _note_player = new NotePlayer (route_ui->midi_track ());
1608 _note_player->add (note);
1609 _note_player->on ();
1613 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1615 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1619 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1621 if (!route_ui || !route_ui->midi_track()) {
1625 delete _note_player;
1626 _note_player = new NotePlayer (route_ui->midi_track());
1628 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1629 _note_player->add (*n);
1632 _note_player->on ();
1637 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1639 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1640 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1642 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1643 (note->note() <= midi_stream_view()->highest_note());
1648 /** Update a canvas note's size from its model note.
1649 * @param ev Canvas note to update.
1650 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1653 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1655 boost::shared_ptr<NoteType> note = ev->note();
1656 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1657 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1662 /* trim note display to not overlap the end of its region */
1664 if (note->length() > 0) {
1665 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1666 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1668 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1671 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1673 if (!note->length()) {
1674 if (_active_notes && note->note() < 128) {
1675 // If this note is already active there's a stuck note,
1676 // finish the old note rectangle
1677 if (_active_notes[note->note()]) {
1678 Note* const old_rect = _active_notes[note->note()];
1679 boost::shared_ptr<NoteType> old_note = old_rect->note();
1680 old_rect->set_x1 (x);
1681 old_rect->set_outline_all ();
1683 _active_notes[note->note()] = ev;
1685 /* outline all but right edge */
1686 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1687 ArdourCanvas::Rectangle::TOP|
1688 ArdourCanvas::Rectangle::LEFT|
1689 ArdourCanvas::Rectangle::BOTTOM));
1691 /* outline all edges */
1692 ev->set_outline_all ();
1695 if (update_ghost_regions) {
1696 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1697 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1699 gr->update_note (ev);
1706 MidiRegionView::update_hit (Hit* ev)
1708 boost::shared_ptr<NoteType> note = ev->note();
1710 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1711 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1712 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1713 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1715 ev->set_position (ArdourCanvas::Duple (x, y));
1716 ev->set_height (diamond_size);
1719 /** Add a MIDI note to the view (with length).
1721 * If in sustained mode, notes with length 0 will be considered active
1722 * notes, and resolve_note should be called when the corresponding note off
1723 * event arrives, to properly display the note.
1726 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1728 NoteBase* event = 0;
1730 if (midi_view()->note_mode() == Sustained) {
1732 Note* ev_rect = new Note (*this, _note_group, note);
1734 update_note (ev_rect);
1738 MidiGhostRegion* gr;
1740 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1741 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1742 gr->add_note(ev_rect);
1746 } else if (midi_view()->note_mode() == Percussive) {
1748 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1750 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1752 update_hit (ev_diamond);
1761 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1762 note_selected(event, true);
1765 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1766 event->show_velocity();
1769 event->on_channel_selection_change (get_selected_channels());
1770 _events.push_back(event);
1779 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1780 MidiStreamView* const view = mtv->midi_view();
1782 view->update_note_range (note->note());
1786 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1787 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1789 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1791 /* potentially extend region to hold new note */
1793 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1794 framepos_t region_end = _region->last_frame();
1796 if (end_frame > region_end) {
1797 _region->set_length (end_frame - _region->position());
1800 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1801 MidiStreamView* const view = mtv->midi_view();
1803 view->update_note_range(new_note->note());
1805 _marked_for_selection.clear ();
1808 start_note_diff_command (_("step add"));
1809 note_diff_add_note (new_note, true, false);
1812 // last_step_edit_note = new_note;
1816 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1818 change_note_lengths (false, false, beats, false, true);
1821 /** Add a new patch change flag to the canvas.
1822 * @param patch the patch change to add
1823 * @param the text to display in the flag
1824 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1827 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1829 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1830 const double x = trackview.editor().sample_to_pixel (region_frames);
1832 double const height = midi_stream_view()->contents_height();
1834 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1835 // so we need to do something more sophisticated to keep its color
1836 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1839 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1840 new PatchChange(*this, group,
1847 if (patch_change->item().width() < _pixel_width) {
1848 // Show unless patch change is beyond the region bounds
1849 if (region_frames < 0 || region_frames >= _region->length()) {
1850 patch_change->hide();
1852 patch_change->show();
1855 patch_change->hide ();
1858 _patch_changes.push_back (patch_change);
1861 MIDI::Name::PatchPrimaryKey
1862 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1864 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1867 /// Return true iff @p pc applies to the given time on the given channel.
1869 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1871 return pc->time() <= time && pc->channel() == channel;
1875 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1877 // The earliest event not before time
1878 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1880 // Go backwards until we find the latest PC for this channel, or the start
1881 while (i != _model->patch_changes().begin() &&
1882 (i == _model->patch_changes().end() ||
1883 !patch_applies(*i, time, channel))) {
1887 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1888 key.set_bank((*i)->bank());
1889 key.set_program((*i)->program ());
1897 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1899 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1901 if (pc.patch()->program() != new_patch.program()) {
1902 c->change_program (pc.patch (), new_patch.program());
1905 int const new_bank = new_patch.bank();
1906 if (pc.patch()->bank() != new_bank) {
1907 c->change_bank (pc.patch (), new_bank);
1910 _model->apply_command (*trackview.session(), c);
1912 _patch_changes.clear ();
1913 display_patch_changes ();
1917 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1919 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1921 if (old_change->time() != new_change.time()) {
1922 c->change_time (old_change, new_change.time());
1925 if (old_change->channel() != new_change.channel()) {
1926 c->change_channel (old_change, new_change.channel());
1929 if (old_change->program() != new_change.program()) {
1930 c->change_program (old_change, new_change.program());
1933 if (old_change->bank() != new_change.bank()) {
1934 c->change_bank (old_change, new_change.bank());
1937 _model->apply_command (*trackview.session(), c);
1939 _patch_changes.clear ();
1940 display_patch_changes ();
1943 /** Add a patch change to the region.
1944 * @param t Time in frames relative to region position
1945 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1946 * MidiTimeAxisView::get_channel_for_add())
1949 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1951 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1953 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1954 c->add (MidiModel::PatchChangePtr (
1955 new Evoral::PatchChange<Evoral::MusicalTime> (
1956 absolute_frames_to_source_beats (_region->position() + t),
1957 mtv->get_channel_for_add(), patch.program(), patch.bank()
1962 _model->apply_command (*trackview.session(), c);
1964 _patch_changes.clear ();
1965 display_patch_changes ();
1969 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1971 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1972 c->change_time (pc.patch (), t);
1973 _model->apply_command (*trackview.session(), c);
1975 _patch_changes.clear ();
1976 display_patch_changes ();
1980 MidiRegionView::delete_patch_change (PatchChange* pc)
1982 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1983 c->remove (pc->patch ());
1984 _model->apply_command (*trackview.session(), c);
1986 _patch_changes.clear ();
1987 display_patch_changes ();
1991 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
1993 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
1995 key.set_bank(key.bank() + delta);
1997 key.set_program(key.program() + delta);
1999 change_patch_change(patch, key);
2003 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2005 if (_selection.empty()) {
2009 _selection.erase (cne);
2013 MidiRegionView::delete_selection()
2015 if (_selection.empty()) {
2019 start_note_diff_command (_("delete selection"));
2021 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2022 if ((*i)->selected()) {
2023 _note_diff_command->remove((*i)->note());
2033 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2035 start_note_diff_command (_("delete note"));
2036 _note_diff_command->remove (n);
2039 trackview.editor().verbose_cursor()->hide ();
2043 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2045 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2047 Selection::iterator tmp = i;
2050 (*i)->set_selected (false);
2051 (*i)->hide_velocity ();
2052 _selection.erase (i);
2060 if (!ev && _entered) {
2061 // Clearing selection entirely, ungrab keyboard
2062 Keyboard::magic_widget_drop_focus();
2063 _grabbed_keyboard = false;
2066 /* this does not change the status of this regionview w.r.t the editor
2071 SelectionCleared (this); /* EMIT SIGNAL */
2076 MidiRegionView::unique_select(NoteBase* ev)
2078 const bool selection_was_empty = _selection.empty();
2080 clear_selection_except (ev);
2082 /* don't bother with checking to see if we should remove this
2083 regionview from the editor selection, since we're about to add
2084 another note, and thus put/keep this regionview in the editor
2088 if (!ev->selected()) {
2089 add_to_selection (ev);
2090 if (selection_was_empty && _entered) {
2091 // Grab keyboard for moving notes with arrow keys
2092 Keyboard::magic_widget_grab_focus();
2093 _grabbed_keyboard = true;
2099 MidiRegionView::select_all_notes ()
2103 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2104 add_to_selection (*i);
2109 MidiRegionView::select_range (framepos_t start, framepos_t end)
2113 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2114 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2115 if (t >= start && t <= end) {
2116 add_to_selection (*i);
2122 MidiRegionView::invert_selection ()
2124 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2125 if ((*i)->selected()) {
2126 remove_from_selection(*i);
2128 add_to_selection (*i);
2134 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2136 bool have_selection = !_selection.empty();
2137 uint8_t low_note = 127;
2138 uint8_t high_note = 0;
2139 MidiModel::Notes& notes (_model->notes());
2140 _optimization_iterator = _events.begin();
2142 if (extend && !have_selection) {
2146 /* scan existing selection to get note range */
2148 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2149 if ((*i)->note()->note() < low_note) {
2150 low_note = (*i)->note()->note();
2152 if ((*i)->note()->note() > high_note) {
2153 high_note = (*i)->note()->note();
2160 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2161 /* only note previously selected is the one we are
2162 * reselecting. treat this as cancelling the selection.
2169 low_note = min (low_note, notenum);
2170 high_note = max (high_note, notenum);
2173 _no_sound_notes = true;
2175 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2177 boost::shared_ptr<NoteType> note (*n);
2179 bool select = false;
2181 if (((1 << note->channel()) & channel_mask) != 0) {
2183 if ((note->note() >= low_note && note->note() <= high_note)) {
2186 } else if (note->note() == notenum) {
2192 if ((cne = find_canvas_note (note)) != 0) {
2193 // extend is false because we've taken care of it,
2194 // since it extends by time range, not pitch.
2195 note_selected (cne, add, false);
2199 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2203 _no_sound_notes = false;
2207 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2209 MidiModel::Notes& notes (_model->notes());
2210 _optimization_iterator = _events.begin();
2212 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2214 boost::shared_ptr<NoteType> note (*n);
2217 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2218 if ((cne = find_canvas_note (note)) != 0) {
2219 if (cne->selected()) {
2220 note_deselected (cne);
2222 note_selected (cne, true, false);
2230 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2233 clear_selection_except (ev);
2234 if (!_selection.empty()) {
2235 PublicEditor& editor (trackview.editor());
2236 editor.get_selection().add (this);
2242 if (!ev->selected()) {
2243 add_to_selection (ev);
2247 /* find end of latest note selected, select all between that and the start of "ev" */
2249 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2250 Evoral::MusicalTime latest = Evoral::MusicalTime();
2252 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2253 if ((*i)->note()->end_time() > latest) {
2254 latest = (*i)->note()->end_time();
2256 if ((*i)->note()->time() < earliest) {
2257 earliest = (*i)->note()->time();
2261 if (ev->note()->end_time() > latest) {
2262 latest = ev->note()->end_time();
2265 if (ev->note()->time() < earliest) {
2266 earliest = ev->note()->time();
2269 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2271 /* find notes entirely within OR spanning the earliest..latest range */
2273 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2274 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2275 add_to_selection (*i);
2283 MidiRegionView::note_deselected(NoteBase* ev)
2285 remove_from_selection (ev);
2289 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2291 PublicEditor& editor = trackview.editor();
2293 // Convert to local coordinates
2294 const framepos_t p = _region->position();
2295 const double y = midi_view()->y_position();
2296 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2297 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2298 const double y0 = max(0.0, gy0 - y);
2299 const double y1 = max(0.0, gy1 - y);
2301 // TODO: Make this faster by storing the last updated selection rect, and only
2302 // adjusting things that are in the area that appears/disappeared.
2303 // We probably need a tree to be able to find events in O(log(n)) time.
2305 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2306 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2307 // Rectangles intersect
2308 if (!(*i)->selected()) {
2309 add_to_selection (*i);
2311 } else if ((*i)->selected() && !extend) {
2312 // Rectangles do not intersect
2313 remove_from_selection (*i);
2317 typedef RouteTimeAxisView::AutomationTracks ATracks;
2318 typedef std::list<Selectable*> Selectables;
2320 /* Add control points to selection. */
2321 const ATracks& atracks = midi_view()->automation_tracks();
2322 Selectables selectables;
2323 editor.get_selection().clear_points();
2324 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2325 a->second->get_selectables(start, end, gy0, gy1, selectables);
2326 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2327 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2329 editor.get_selection().add(cp);
2332 a->second->set_selected_points(editor.get_selection().points);
2337 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2343 // TODO: Make this faster by storing the last updated selection rect, and only
2344 // adjusting things that are in the area that appears/disappeared.
2345 // We probably need a tree to be able to find events in O(log(n)) time.
2347 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2348 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2349 // within y- (note-) range
2350 if (!(*i)->selected()) {
2351 add_to_selection (*i);
2353 } else if ((*i)->selected() && !extend) {
2354 remove_from_selection (*i);
2360 MidiRegionView::remove_from_selection (NoteBase* ev)
2362 Selection::iterator i = _selection.find (ev);
2364 if (i != _selection.end()) {
2365 _selection.erase (i);
2366 if (_selection.empty() && _grabbed_keyboard) {
2368 Keyboard::magic_widget_drop_focus();
2369 _grabbed_keyboard = false;
2373 ev->set_selected (false);
2374 ev->hide_velocity ();
2376 if (_selection.empty()) {
2377 PublicEditor& editor (trackview.editor());
2378 editor.get_selection().remove (this);
2383 MidiRegionView::add_to_selection (NoteBase* ev)
2385 const bool selection_was_empty = _selection.empty();
2387 if (_selection.insert (ev).second) {
2388 ev->set_selected (true);
2389 start_playing_midi_note ((ev)->note());
2390 if (selection_was_empty && _entered) {
2391 // Grab keyboard for moving notes with arrow keys
2392 Keyboard::magic_widget_grab_focus();
2393 _grabbed_keyboard = true;
2397 if (selection_was_empty) {
2398 PublicEditor& editor (trackview.editor());
2399 editor.get_selection().add (this);
2404 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2406 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2407 PossibleChord to_play;
2408 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2410 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2411 if ((*i)->note()->time() < earliest) {
2412 earliest = (*i)->note()->time();
2416 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2417 if ((*i)->note()->time() == earliest) {
2418 to_play.push_back ((*i)->note());
2420 (*i)->move_event(dx, dy);
2423 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2425 if (to_play.size() > 1) {
2427 PossibleChord shifted;
2429 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2430 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2431 moved_note->set_note (moved_note->note() + cumulative_dy);
2432 shifted.push_back (moved_note);
2435 start_playing_midi_chord (shifted);
2437 } else if (!to_play.empty()) {
2439 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2440 moved_note->set_note (moved_note->note() + cumulative_dy);
2441 start_playing_midi_note (moved_note);
2447 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2449 uint8_t lowest_note_in_selection = 127;
2450 uint8_t highest_note_in_selection = 0;
2451 uint8_t highest_note_difference = 0;
2453 // find highest and lowest notes first
2455 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2456 uint8_t pitch = (*i)->note()->note();
2457 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2458 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2462 cerr << "dnote: " << (int) dnote << endl;
2463 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2464 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2465 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2466 << int(highest_note_in_selection) << endl;
2467 cerr << "selection size: " << _selection.size() << endl;
2468 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2471 // Make sure the note pitch does not exceed the MIDI standard range
2472 if (highest_note_in_selection + dnote > 127) {
2473 highest_note_difference = highest_note_in_selection - 127;
2476 start_note_diff_command (_("move notes"));
2478 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2480 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2481 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2487 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2489 uint8_t original_pitch = (*i)->note()->note();
2490 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2492 // keep notes in standard midi range
2493 clamp_to_0_127(new_pitch);
2495 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2496 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2498 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2503 // care about notes being moved beyond the upper/lower bounds on the canvas
2504 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2505 highest_note_in_selection > midi_stream_view()->highest_note()) {
2506 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2510 /** @param x Pixel relative to the region position.
2511 * @return Snapped frame relative to the region position.
2514 MidiRegionView::snap_pixel_to_sample(double x)
2516 PublicEditor& editor (trackview.editor());
2517 return snap_frame_to_frame (editor.pixel_to_sample (x));
2520 /** @param x Pixel relative to the region position.
2521 * @return Snapped pixel relative to the region position.
2524 MidiRegionView::snap_to_pixel(double x)
2526 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2530 MidiRegionView::get_position_pixels()
2532 framepos_t region_frame = get_position();
2533 return trackview.editor().sample_to_pixel(region_frame);
2537 MidiRegionView::get_end_position_pixels()
2539 framepos_t frame = get_position() + get_duration ();
2540 return trackview.editor().sample_to_pixel(frame);
2544 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2546 /* the time converter will return the frame corresponding to `beats'
2547 relative to the start of the source. The start of the source
2548 is an implied position given by region->position - region->start
2550 const framepos_t source_start = _region->position() - _region->start();
2551 return source_start + _source_relative_time_converter.to (beats);
2555 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2557 /* the `frames' argument needs to be converted into a frame count
2558 relative to the start of the source before being passed in to the
2561 const framepos_t source_start = _region->position() - _region->start();
2562 return _source_relative_time_converter.from (frames - source_start);
2566 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2568 return _region_relative_time_converter.to(beats);
2572 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2574 return _region_relative_time_converter.from(frames);
2578 MidiRegionView::begin_resizing (bool /*at_front*/)
2580 _resize_data.clear();
2582 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2583 Note *note = dynamic_cast<Note*> (*i);
2585 // only insert CanvasNotes into the map
2587 NoteResizeData *resize_data = new NoteResizeData();
2588 resize_data->note = note;
2590 // create a new SimpleRect from the note which will be the resize preview
2591 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2592 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2594 // calculate the colors: get the color settings
2595 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2596 ARDOUR_UI::config()->color ("midi note selected"),
2599 // make the resize preview notes more transparent and bright
2600 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2602 // calculate color based on note velocity
2603 resize_rect->set_fill_color (UINT_INTERPOLATE(
2604 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2608 resize_rect->set_outline_color (NoteBase::calculate_outline (
2609 ARDOUR_UI::config()->color ("midi note selected")));
2611 resize_data->resize_rect = resize_rect;
2612 _resize_data.push_back(resize_data);
2617 /** Update resizing notes while user drags.
2618 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2619 * @param at_front which end of the note (true == note on, false == note off)
2620 * @param delta_x change in mouse position since the start of the drag
2621 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2622 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2623 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2624 * as the \a primary note.
2627 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2629 bool cursor_set = false;
2631 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2632 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2633 Note* canvas_note = (*i)->note;
2638 current_x = canvas_note->x0() + delta_x;
2640 current_x = primary->x0() + delta_x;
2644 current_x = canvas_note->x1() + delta_x;
2646 current_x = primary->x1() + delta_x;
2650 if (current_x < 0) {
2651 // This works even with snapping because RegionView::snap_frame_to_frame()
2652 // snaps forward if the snapped sample is before the beginning of the region
2655 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2656 current_x = trackview.editor().sample_to_pixel(_region->length());
2660 resize_rect->set_x0 (snap_to_pixel(current_x));
2661 resize_rect->set_x1 (canvas_note->x1());
2663 resize_rect->set_x1 (snap_to_pixel(current_x));
2664 resize_rect->set_x0 (canvas_note->x0());
2668 const double snapped_x = snap_pixel_to_sample (current_x);
2669 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2670 Evoral::MusicalTime len = Evoral::MusicalTime();
2673 if (beats < canvas_note->note()->end_time()) {
2674 len = canvas_note->note()->time() - beats;
2675 len += canvas_note->note()->length();
2678 if (beats >= canvas_note->note()->time()) {
2679 len = beats - canvas_note->note()->time();
2684 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2685 show_verbose_cursor (buf, 0, 0);
2694 /** Finish resizing notes when the user releases the mouse button.
2695 * Parameters the same as for \a update_resizing().
2698 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2700 start_note_diff_command (_("resize notes"));
2702 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2703 Note* canvas_note = (*i)->note;
2704 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2706 /* Get the new x position for this resize, which is in pixels relative
2707 * to the region position.
2714 current_x = canvas_note->x0() + delta_x;
2716 current_x = primary->x0() + delta_x;
2720 current_x = canvas_note->x1() + delta_x;
2722 current_x = primary->x1() + delta_x;
2726 if (current_x < 0) {
2729 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2730 current_x = trackview.editor().sample_to_pixel(_region->length());
2733 /* Convert that to a frame within the source */
2734 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2736 /* and then to beats */
2737 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2739 if (at_front && x_beats < canvas_note->note()->end_time()) {
2740 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2742 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2743 len += canvas_note->note()->length();
2746 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2751 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2754 /* XXX convert to beats */
2755 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2763 _resize_data.clear();
2768 MidiRegionView::abort_resizing ()
2770 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2771 delete (*i)->resize_rect;
2775 _resize_data.clear ();
2779 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2781 uint8_t new_velocity;
2784 new_velocity = event->note()->velocity() + velocity;
2785 clamp_to_0_127(new_velocity);
2787 new_velocity = velocity;
2790 event->set_selected (event->selected()); // change color
2792 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2796 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2801 new_note = event->note()->note() + note;
2806 clamp_to_0_127 (new_note);
2807 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2811 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2813 bool change_start = false;
2814 bool change_length = false;
2815 Evoral::MusicalTime new_start;
2816 Evoral::MusicalTime new_length;
2818 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2820 front_delta: if positive - move the start of the note later in time (shortening it)
2821 if negative - move the start of the note earlier in time (lengthening it)
2823 end_delta: if positive - move the end of the note later in time (lengthening it)
2824 if negative - move the end of the note earlier in time (shortening it)
2827 if (!!front_delta) {
2828 if (front_delta < 0) {
2830 if (event->note()->time() < -front_delta) {
2831 new_start = Evoral::MusicalTime();
2833 new_start = event->note()->time() + front_delta; // moves earlier
2836 /* start moved toward zero, so move the end point out to where it used to be.
2837 Note that front_delta is negative, so this increases the length.
2840 new_length = event->note()->length() - front_delta;
2841 change_start = true;
2842 change_length = true;
2846 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2848 if (new_pos < event->note()->end_time()) {
2849 new_start = event->note()->time() + front_delta;
2850 /* start moved toward the end, so move the end point back to where it used to be */
2851 new_length = event->note()->length() - front_delta;
2852 change_start = true;
2853 change_length = true;
2860 bool can_change = true;
2861 if (end_delta < 0) {
2862 if (event->note()->length() < -end_delta) {
2868 new_length = event->note()->length() + end_delta;
2869 change_length = true;
2874 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2877 if (change_length) {
2878 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2883 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2885 uint8_t new_channel;
2889 if (event->note()->channel() < -chn) {
2892 new_channel = event->note()->channel() + chn;
2895 new_channel = event->note()->channel() + chn;
2898 new_channel = (uint8_t) chn;
2901 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2905 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2907 Evoral::MusicalTime new_time;
2911 if (event->note()->time() < -delta) {
2912 new_time = Evoral::MusicalTime();
2914 new_time = event->note()->time() + delta;
2917 new_time = event->note()->time() + delta;
2923 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2927 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2929 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2933 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2938 if (_selection.empty()) {
2953 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2954 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2960 start_note_diff_command (_("change velocities"));
2962 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2963 Selection::iterator next = i;
2967 if (i == _selection.begin()) {
2968 change_note_velocity (*i, delta, true);
2969 value = (*i)->note()->velocity() + delta;
2971 change_note_velocity (*i, value, false);
2975 change_note_velocity (*i, delta, true);
2984 if (!_selection.empty()) {
2986 snprintf (buf, sizeof (buf), "Vel %d",
2987 (int) (*_selection.begin())->note()->velocity());
2988 show_verbose_cursor (buf, 10, 10);
2994 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2996 if (_selection.empty()) {
3013 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3015 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3019 if ((int8_t) (*i)->note()->note() + delta > 127) {
3026 start_note_diff_command (_("transpose"));
3028 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3029 Selection::iterator next = i;
3031 change_note_note (*i, delta, true);
3039 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3043 delta = Evoral::MusicalTime(1.0/128.0);
3045 /* grab the current grid distance */
3046 delta = get_grid_beats(_region->position());
3054 start_note_diff_command (_("change note lengths"));
3056 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3057 Selection::iterator next = i;
3060 /* note the negation of the delta for start */
3063 (start ? -delta : Evoral::MusicalTime()),
3064 (end ? delta : Evoral::MusicalTime()));
3073 MidiRegionView::nudge_notes (bool forward, bool fine)
3075 if (_selection.empty()) {
3079 /* pick a note as the point along the timeline to get the nudge distance.
3080 its not necessarily the earliest note, so we may want to pull the notes out
3081 into a vector and sort before using the first one.
3084 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3085 Evoral::MusicalTime delta;
3089 /* non-fine, move by 1 bar regardless of snap */
3090 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3092 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3094 /* grid is off - use nudge distance */
3097 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3098 delta = region_frames_to_region_beats (fabs ((double)distance));
3104 framepos_t next_pos = ref_point;
3107 if (max_framepos - 1 < next_pos) {
3111 if (next_pos == 0) {
3117 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3118 const framecnt_t distance = ref_point - next_pos;
3119 delta = region_frames_to_region_beats (fabs ((double)distance));
3130 start_note_diff_command (_("nudge"));
3132 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3133 Selection::iterator next = i;
3135 change_note_time (*i, delta, true);
3143 MidiRegionView::change_channel(uint8_t channel)
3145 start_note_diff_command(_("change channel"));
3146 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3147 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3155 MidiRegionView::note_entered(NoteBase* ev)
3157 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3159 pre_note_enter_cursor = editor->get_canvas_cursor ();
3161 if (_mouse_state == SelectTouchDragging) {
3162 note_selected (ev, true);
3165 show_verbose_cursor (ev->note ());
3169 MidiRegionView::note_left (NoteBase*)
3171 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3173 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3174 (*i)->hide_velocity ();
3177 editor->verbose_cursor()->hide ();
3179 if (pre_note_enter_cursor) {
3180 editor->set_canvas_cursor (pre_note_enter_cursor);
3181 pre_note_enter_cursor = 0;
3186 MidiRegionView::patch_entered (PatchChange* p)
3189 /* XXX should get patch name if we can */
3190 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3191 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3192 << _("Channel ") << ((int) p->patch()->channel() + 1);
3193 show_verbose_cursor (s.str(), 10, 20);
3194 p->item().grab_focus();
3198 MidiRegionView::patch_left (PatchChange *)
3200 trackview.editor().verbose_cursor()->hide ();
3201 /* focus will transfer back via the enter-notify event sent to this
3207 MidiRegionView::sysex_entered (SysEx* p)
3211 // need a way to extract text from p->_flag->_text
3213 // show_verbose_cursor (s.str(), 10, 20);
3214 p->item().grab_focus();
3218 MidiRegionView::sysex_left (SysEx *)
3220 trackview.editor().verbose_cursor()->hide ();
3221 /* focus will transfer back via the enter-notify event sent to this
3227 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3229 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3230 Editing::MouseMode mm = editor->current_mouse_mode();
3231 bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3233 if (can_set_cursor) {
3234 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3235 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3236 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3237 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3238 } else if (pre_note_enter_cursor) {
3239 editor->set_canvas_cursor (pre_note_enter_cursor);
3245 MidiRegionView::fill_opacity() const
3247 uint32_t a = RegionView::fill_opacity();
3248 if (trackview.editor().current_mouse_mode() == MouseDraw ||
3249 trackview.editor().current_mouse_mode() == MouseContent) {
3250 /* Make rect more transparent when in an internal mode. This should
3251 probably be configurable somehow. */
3258 MidiRegionView::set_frame_color()
3262 TimeAxisViewItem::set_frame_color ();
3269 f = ARDOUR_UI::config()->color ("selected region base");
3270 } else if (high_enough_for_name || !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3271 f = ARDOUR_UI::config()->color_mod ("midi frame base", "midi frame base");
3276 f = UINT_RGBA_CHANGE_A (f, fill_opacity());
3278 frame->set_fill_color (f);
3282 MidiRegionView::midi_channel_mode_changed ()
3284 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3285 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3286 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3288 if (mode == ForceChannel) {
3289 mask = 0xFFFF; // Show all notes as active (below)
3292 // Update notes for selection
3293 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3294 (*i)->on_channel_selection_change (mask);
3297 _patch_changes.clear ();
3298 display_patch_changes ();
3302 MidiRegionView::instrument_settings_changed ()
3308 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3310 if (_selection.empty()) {
3314 PublicEditor& editor (trackview.editor());
3318 /* XXX what to do ? */
3322 editor.get_cut_buffer().add (selection_as_cut_buffer());
3330 start_note_diff_command();
3332 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3339 note_diff_remove_note (*i);
3349 MidiRegionView::selection_as_cut_buffer () const
3353 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3354 NoteType* n = (*i)->note().get();
3355 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3358 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3364 /** This method handles undo */
3366 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3368 // Paste notes, if available
3369 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3370 if (m != selection.midi_notes.end()) {
3371 ctx.counts.increase_n_notes();
3372 paste_internal(pos, ctx.count, ctx.times, **m);
3375 // Paste control points to automation children, if available
3376 typedef RouteTimeAxisView::AutomationTracks ATracks;
3377 const ATracks& atracks = midi_view()->automation_tracks();
3378 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3379 a->second->paste(pos, selection, ctx);
3385 /** This method handles undo */
3387 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3393 start_note_diff_command (_("paste"));
3395 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3396 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3397 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3398 const Evoral::MusicalTime duration = last_time - first_time;
3399 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3400 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3401 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3402 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3404 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3407 duration, pos, _region->position(),
3412 for (int n = 0; n < (int) times; ++n) {
3414 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3416 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3417 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3419 /* make all newly added notes selected */
3421 note_diff_add_note (copied_note, true);
3422 end_point = copied_note->end_time();
3426 /* if we pasted past the current end of the region, extend the region */
3428 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3429 framepos_t region_end = _region->position() + _region->length() - 1;
3431 if (end_frame > region_end) {
3433 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3435 _region->clear_changes ();
3436 _region->set_length (end_frame - _region->position());
3437 trackview.session()->add_command (new StatefulDiffCommand (_region));
3443 struct EventNoteTimeEarlyFirstComparator {
3444 bool operator() (NoteBase* a, NoteBase* b) {
3445 return a->note()->time() < b->note()->time();
3450 MidiRegionView::time_sort_events ()
3452 if (!_sort_needed) {
3456 EventNoteTimeEarlyFirstComparator cmp;
3459 _sort_needed = false;
3463 MidiRegionView::goto_next_note (bool add_to_selection)
3465 bool use_next = false;
3467 if (_events.back()->selected()) {
3471 time_sort_events ();
3473 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3474 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3476 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3477 if ((*i)->selected()) {
3480 } else if (use_next) {
3481 if (channel_mask & (1 << (*i)->note()->channel())) {
3482 if (!add_to_selection) {
3485 note_selected (*i, true, false);
3492 /* use the first one */
3494 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3495 unique_select (_events.front());
3500 MidiRegionView::goto_previous_note (bool add_to_selection)
3502 bool use_next = false;
3504 if (_events.front()->selected()) {
3508 time_sort_events ();
3510 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3511 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3513 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3514 if ((*i)->selected()) {
3517 } else if (use_next) {
3518 if (channel_mask & (1 << (*i)->note()->channel())) {
3519 if (!add_to_selection) {
3522 note_selected (*i, true, false);
3529 /* use the last one */
3531 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3532 unique_select (*(_events.rbegin()));
3537 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3539 bool had_selected = false;
3541 time_sort_events ();
3543 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3544 if ((*i)->selected()) {
3545 selected.insert ((*i)->note());
3546 had_selected = true;
3550 if (allow_all_if_none_selected && !had_selected) {
3551 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3552 selected.insert ((*i)->note());
3558 MidiRegionView::update_ghost_note (double x, double y)
3560 x = std::max(0.0, x);
3562 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3567 _note_group->canvas_to_item (x, y);
3569 PublicEditor& editor = trackview.editor ();
3571 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3572 framecnt_t grid_frames;
3573 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3575 /* use region_frames... because we are converting a delta within the region
3578 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3580 /* note that this sets the time of the ghost note in beats relative to
3581 the start of the source; that is how all note times are stored.
3583 _ghost_note->note()->set_time (
3584 std::max(Evoral::MusicalTime(),
3585 absolute_frames_to_source_beats (f + _region->position ())));
3586 _ghost_note->note()->set_length (length);
3587 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3588 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3590 /* the ghost note does not appear in ghost regions, so pass false in here */
3591 update_note (_ghost_note, false);
3593 show_verbose_cursor (_ghost_note->note ());
3597 MidiRegionView::create_ghost_note (double x, double y)
3599 remove_ghost_note ();
3601 boost::shared_ptr<NoteType> g (new NoteType);
3602 _ghost_note = new Note (*this, _note_group, g);
3603 _ghost_note->set_ignore_events (true);
3604 _ghost_note->set_outline_color (0x000000aa);
3605 update_ghost_note (x, y);
3606 _ghost_note->show ();
3608 show_verbose_cursor (_ghost_note->note ());
3612 MidiRegionView::remove_ghost_note ()
3619 MidiRegionView::snap_changed ()
3625 create_ghost_note (_last_ghost_x, _last_ghost_y);
3629 MidiRegionView::drop_down_keys ()
3631 _mouse_state = None;
3635 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3637 /* XXX: This is dead code. What was it for? */
3639 double note = midi_stream_view()->y_to_note(y);
3641 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3643 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3645 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3646 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3647 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3648 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3653 bool add_mrv_selection = false;
3655 if (_selection.empty()) {
3656 add_mrv_selection = true;
3659 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3660 if (_selection.insert (*i).second) {
3661 (*i)->set_selected (true);
3665 if (add_mrv_selection) {
3666 PublicEditor& editor (trackview.editor());
3667 editor.get_selection().add (this);
3672 MidiRegionView::color_handler ()
3674 RegionView::color_handler ();
3676 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3677 (*i)->set_selected ((*i)->selected()); // will change color
3680 /* XXX probably more to do here */
3684 MidiRegionView::enable_display (bool yn)
3686 RegionView::enable_display (yn);
3693 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3695 if (_step_edit_cursor == 0) {
3696 ArdourCanvas::Item* const group = get_canvas_group();
3698 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3699 _step_edit_cursor->set_y0 (0);
3700 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3701 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3702 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3705 move_step_edit_cursor (pos);
3706 _step_edit_cursor->show ();
3710 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3712 _step_edit_cursor_position = pos;
3714 if (_step_edit_cursor) {
3715 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3716 _step_edit_cursor->set_x0 (pixel);
3717 set_step_edit_cursor_width (_step_edit_cursor_width);
3722 MidiRegionView::hide_step_edit_cursor ()
3724 if (_step_edit_cursor) {
3725 _step_edit_cursor->hide ();
3730 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3732 _step_edit_cursor_width = beats;
3734 if (_step_edit_cursor) {
3735 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3739 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3740 * @param w Source that the data will end up in.
3743 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3745 if (!_active_notes) {
3746 /* we aren't actively being recorded to */
3750 boost::shared_ptr<MidiSource> src = w.lock ();
3751 if (!src || src != midi_region()->midi_source()) {
3752 /* recorded data was not destined for our source */
3756 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3758 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3760 framepos_t back = max_framepos;
3762 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3763 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3765 if (ev.is_channel_event()) {
3766 if (get_channel_mode() == FilterChannels) {
3767 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3773 /* convert from session frames to source beats */
3774 Evoral::MusicalTime const time_beats = _source_relative_time_converter.from(ev.time());
3776 if (ev.type() == MIDI_CMD_NOTE_ON) {
3777 boost::shared_ptr<NoteType> note (
3778 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3780 add_note (note, true);
3782 /* fix up our note range */
3783 if (ev.note() < _current_range_min) {
3784 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3785 } else if (ev.note() > _current_range_max) {
3786 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3789 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3790 resolve_note (ev.note (), time_beats);
3796 midi_stream_view()->check_record_layers (region(), back);
3800 MidiRegionView::trim_front_starting ()
3802 /* Reparent the note group to the region view's parent, so that it doesn't change
3803 when the region view is trimmed.
3805 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3806 _temporary_note_group->move (group->position ());
3807 _note_group->reparent (_temporary_note_group);
3811 MidiRegionView::trim_front_ending ()
3813 _note_group->reparent (group);
3814 delete _temporary_note_group;
3815 _temporary_note_group = 0;
3817 if (_region->start() < 0) {
3818 /* Trim drag made start time -ve; fix this */
3819 midi_region()->fix_negative_start ();
3824 MidiRegionView::edit_patch_change (PatchChange* pc)
3826 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3828 int response = d.run();
3831 case Gtk::RESPONSE_ACCEPT:
3833 case Gtk::RESPONSE_REJECT:
3834 delete_patch_change (pc);
3840 change_patch_change (pc->patch(), d.patch ());
3844 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3847 // sysyex object doesn't have a pointer to a sysex event
3848 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3849 // c->remove (sysex->sysex());
3850 // _model->apply_command (*trackview.session(), c);
3852 //_sys_exes.clear ();
3853 // display_sysexes();
3857 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3859 using namespace MIDI::Name;
3863 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3865 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3867 MIDI::Name::PatchPrimaryKey patch_key;
3868 get_patch_key_at(n->time(), n->channel(), patch_key);
3869 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3872 patch_key.program(),
3878 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3880 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3881 (int) n->channel() + 1,
3882 (int) n->velocity());
3884 show_verbose_cursor(buf, 10, 20);
3888 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3890 trackview.editor().verbose_cursor()->set (text);
3891 trackview.editor().verbose_cursor()->show ();
3892 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
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 delete _note_player;
3940 MidiRegionView::get_channel_mode () const
3942 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3943 return rtav->midi_track()->get_playback_channel_mode();
3947 MidiRegionView::get_selected_channels () const
3949 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3950 return rtav->midi_track()->get_playback_channel_mask();
3955 MidiRegionView::get_grid_beats(framepos_t pos) const
3957 PublicEditor& editor = trackview.editor();
3958 bool success = false;
3959 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3961 beats = Evoral::MusicalTime(1);