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.
27 #include <gtkmm2ext/gtk_ui.h>
29 #include <sigc++/signal.h>
31 #include "pbd/memento_command.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "ardour/playlist.h"
35 #include "ardour/tempo.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_model.h"
39 #include "ardour/midi_patch_manager.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIParameters.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "automation_region_view.h"
48 #include "automation_time_axis.h"
49 #include "canvas-hit.h"
50 #include "canvas-note.h"
51 #include "canvas_patch_change.h"
54 #include "ghostregion.h"
55 #include "gui_thread.h"
57 #include "midi_channel_dialog.h"
58 #include "midi_cut_buffer.h"
59 #include "midi_list_editor.h"
60 #include "midi_region_view.h"
61 #include "midi_streamview.h"
62 #include "midi_time_axis.h"
63 #include "midi_util.h"
64 #include "note_player.h"
65 #include "public_editor.h"
66 #include "rgb_macros.h"
67 #include "selection.h"
68 #include "simpleline.h"
69 #include "streamview.h"
71 #include "mouse_cursors.h"
72 #include "patch_change_dialog.h"
73 #include "verbose_cursor.h"
77 using namespace ARDOUR;
79 using namespace Editing;
80 using namespace ArdourCanvas;
81 using Gtkmm2ext::Keyboard;
83 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
85 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
87 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
88 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
89 : RegionView (parent, tv, r, spu, basic_color)
91 , _last_channel_selection(0xFFFF)
92 , _current_range_min(0)
93 , _current_range_max(0)
94 , _model_name(string())
95 , _custom_device_mode(string())
97 , _note_group(new ArdourCanvas::Group(*group))
98 , _note_diff_command (0)
101 , _step_edit_cursor (0)
102 , _step_edit_cursor_width (1.0)
103 , _step_edit_cursor_position (0.0)
104 , _channel_selection_scoped_note (0)
105 , _temporary_note_group (0)
108 , _sort_needed (true)
109 , _optimization_iterator (_events.end())
111 , _no_sound_notes (false)
114 , _pre_enter_cursor (0)
116 _note_group->raise_to_top();
117 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
119 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
120 connect_to_diskstream ();
122 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
125 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
126 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
127 TimeAxisViewItem::Visibility visibility)
128 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
130 , _last_channel_selection(0xFFFF)
131 , _model_name(string())
132 , _custom_device_mode(string())
134 , _note_group(new ArdourCanvas::Group(*parent))
135 , _note_diff_command (0)
138 , _step_edit_cursor (0)
139 , _step_edit_cursor_width (1.0)
140 , _step_edit_cursor_position (0.0)
141 , _channel_selection_scoped_note (0)
142 , _temporary_note_group (0)
145 , _sort_needed (true)
146 , _optimization_iterator (_events.end())
148 , _no_sound_notes (false)
152 _note_group->raise_to_top();
153 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
155 connect_to_diskstream ();
157 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
161 MidiRegionView::parameter_changed (std::string const & p)
163 if (p == "diplay-first-midi-bank-as-zero") {
164 if (_enable_display) {
170 MidiRegionView::MidiRegionView (const MidiRegionView& other)
171 : sigc::trackable(other)
174 , _last_channel_selection(0xFFFF)
175 , _model_name(string())
176 , _custom_device_mode(string())
178 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
179 , _note_diff_command (0)
182 , _step_edit_cursor (0)
183 , _step_edit_cursor_width (1.0)
184 , _step_edit_cursor_position (0.0)
185 , _channel_selection_scoped_note (0)
186 , _temporary_note_group (0)
189 , _sort_needed (true)
190 , _optimization_iterator (_events.end())
192 , _no_sound_notes (false)
199 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
200 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
205 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
206 : RegionView (other, boost::shared_ptr<Region> (region))
208 , _last_channel_selection(0xFFFF)
209 , _model_name(string())
210 , _custom_device_mode(string())
212 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
213 , _note_diff_command (0)
216 , _step_edit_cursor (0)
217 , _step_edit_cursor_width (1.0)
218 , _step_edit_cursor_position (0.0)
219 , _channel_selection_scoped_note (0)
220 , _temporary_note_group (0)
223 , _sort_needed (true)
224 , _optimization_iterator (_events.end())
226 , _no_sound_notes (false)
233 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
234 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
240 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
242 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
244 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
245 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
249 midi_region()->midi_source(0)->load_model();
252 _model = midi_region()->midi_source(0)->model();
253 _enable_display = false;
255 RegionView::init (basic_color, false);
257 compute_colors (basic_color);
259 set_height (trackview.current_height());
262 region_sync_changed ();
263 region_resized (ARDOUR::bounds_change);
266 reset_width_dependent_items (_pixel_width);
270 _enable_display = true;
273 display_model (_model);
277 group->raise_to_top();
278 group->signal_event().connect(
279 sigc::mem_fun(this, &MidiRegionView::canvas_event), false);
281 midi_view()->signal_channel_mode_changed().connect(
282 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
284 midi_view()->signal_midi_patch_settings_changed().connect(
285 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
287 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
288 ui_bind(&MidiRegionView::snap_changed, this),
291 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
292 connect_to_diskstream ();
294 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
297 const boost::shared_ptr<ARDOUR::MidiRegion>
298 MidiRegionView::midi_region() const
300 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
304 MidiRegionView::connect_to_diskstream ()
306 midi_view()->midi_track()->DataRecorded.connect(
307 *this, invalidator(*this),
308 ui_bind(&MidiRegionView::data_recorded, this, _1),
313 MidiRegionView::canvas_event(GdkEvent* ev)
316 case GDK_ENTER_NOTIFY:
317 case GDK_LEAVE_NOTIFY:
318 _last_event_x = ev->crossing.x;
319 _last_event_y = ev->crossing.y;
321 case GDK_MOTION_NOTIFY:
322 _last_event_x = ev->motion.x;
323 _last_event_y = ev->motion.y;
329 if (!trackview.editor().internal_editing()) {
335 return scroll (&ev->scroll);
338 return key_press (&ev->key);
340 case GDK_KEY_RELEASE:
341 return key_release (&ev->key);
343 case GDK_BUTTON_PRESS:
344 return button_press (&ev->button);
346 case GDK_2BUTTON_PRESS:
349 case GDK_BUTTON_RELEASE:
350 return button_release (&ev->button);
352 case GDK_ENTER_NOTIFY:
353 return enter_notify (&ev->crossing);
355 case GDK_LEAVE_NOTIFY:
356 return leave_notify (&ev->crossing);
358 case GDK_MOTION_NOTIFY:
359 return motion (&ev->motion);
369 MidiRegionView::remove_ghost_note ()
376 MidiRegionView::enter_notify (GdkEventCrossing* ev)
378 trackview.editor().MouseModeChanged.connect (
379 _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
382 if (trackview.editor().current_mouse_mode() == MouseRange) {
383 create_ghost_note (ev->x, ev->y);
386 if (!trackview.editor().internal_editing()) {
387 Keyboard::magic_widget_drop_focus();
389 Keyboard::magic_widget_grab_focus();
397 MidiRegionView::leave_notify (GdkEventCrossing*)
399 _mouse_mode_connection.disconnect ();
401 trackview.editor().verbose_cursor()->hide ();
402 remove_ghost_note ();
408 MidiRegionView::mouse_mode_changed ()
410 if (trackview.editor().current_mouse_mode() == MouseRange && trackview.editor().internal_editing()) {
411 create_ghost_note (_last_event_x, _last_event_y);
413 remove_ghost_note ();
414 trackview.editor().verbose_cursor()->hide ();
417 if (!trackview.editor().internal_editing()) {
418 Keyboard::magic_widget_drop_focus();
420 Keyboard::magic_widget_grab_focus();
426 MidiRegionView::button_press (GdkEventButton* ev)
428 if (ev->button != 1) {
435 group->w2i (_last_x, _last_y);
437 if (_mouse_state != SelectTouchDragging) {
439 _pressed_button = ev->button;
440 _mouse_state = Pressed;
445 _pressed_button = ev->button;
451 MidiRegionView::button_release (GdkEventButton* ev)
453 double event_x, event_y;
455 if (ev->button != 1) {
462 group->w2i(event_x, event_y);
463 group->ungrab(ev->time);
465 switch (_mouse_state) {
466 case Pressed: // Clicked
468 switch (trackview.editor().current_mouse_mode()) {
474 if (Keyboard::is_insert_note_event(ev)) {
476 double event_x, event_y;
480 group->w2i(event_x, event_y);
483 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
489 create_note_at (event_x, event_y, beats, true, true);
497 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
503 create_note_at (event_x, event_y, beats, true, true);
514 case SelectRectDragging: // Select drag done
521 case AddDragging: // Add drag done
525 if (Keyboard::is_insert_note_event(ev) || trackview.editor().current_mouse_mode() == MouseRange) {
527 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
529 const double x = _drag_rect->property_x1();
530 const double length = trackview.editor().pixel_to_frame (_drag_rect->property_x2() - _drag_rect->property_x1());
532 create_note_at (x, _drag_rect->property_y1(), region_frames_to_region_beats(length), true, false);
539 create_ghost_note (ev->x, ev->y);
549 MidiRegionView::motion (GdkEventMotion* ev)
551 double event_x, event_y;
552 framepos_t event_frame = 0;
556 group->w2i(event_x, event_y);
558 PublicEditor& editor = trackview.editor ();
560 // convert event_x to global frame
561 framecnt_t grid_frames;
562 event_frame = snap_frame_to_grid_underneath (editor.pixel_to_frame (event_x), grid_frames);
564 if (!_ghost_note && editor.current_mouse_mode() != MouseRange
565 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())
566 && _mouse_state != AddDragging) {
568 create_ghost_note (ev->x, ev->y);
569 } else if (_ghost_note && editor.current_mouse_mode() != MouseRange
570 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
572 update_ghost_note (ev->x, ev->y);
573 } else if (_ghost_note && editor.current_mouse_mode() != MouseRange) {
578 editor.verbose_cursor()->hide ();
579 } else if (_ghost_note && editor.current_mouse_mode() == MouseRange) {
580 update_ghost_note (ev->x, ev->y);
583 /* any motion immediately hides velocity text that may have been visible */
585 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
586 (*i)->hide_velocity ();
589 switch (_mouse_state) {
590 case Pressed: // Maybe start a drag, if we've moved a bit
592 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
593 /* no appreciable movement since the button was pressed */
597 if (_pressed_button == 1 && editor.current_mouse_mode() == MouseObject
598 && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
601 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
602 Gdk::Cursor(Gdk::FLEUR), ev->time);
606 _drag_start_x = event_x;
607 _drag_start_y = event_y;
609 _drag_rect = new ArdourCanvas::SimpleRect(*group);
610 _drag_rect->property_x1() = event_x;
611 _drag_rect->property_y1() = event_y;
612 _drag_rect->property_x2() = event_x;
613 _drag_rect->property_y2() = event_y;
614 _drag_rect->property_outline_what() = 0xFF;
615 _drag_rect->property_outline_color_rgba()
616 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
617 _drag_rect->property_fill_color_rgba()
618 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
620 _mouse_state = SelectRectDragging;
623 } else if (editor.internal_editing()) {
624 // Add note drag start
626 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
627 Gdk::Cursor(Gdk::FLEUR), ev->time);
631 _drag_start_x = event_x;
632 _drag_start_y = event_y;
634 _drag_rect = new ArdourCanvas::SimpleRect(*group);
635 _drag_rect->property_x1() = editor.frame_to_pixel(event_frame);
637 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
638 midi_stream_view()->y_to_note(event_y));
639 _drag_rect->property_x2() = editor.frame_to_pixel(event_frame);
641 _drag_rect->property_y2() = _drag_rect->property_y1()
642 + floor(midi_stream_view()->note_height());
643 _drag_rect->property_outline_what() = 0xFF;
644 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
645 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
647 _mouse_state = AddDragging;
652 editor.verbose_cursor()->hide ();
659 case SelectRectDragging: // Select drag motion
660 case AddDragging: // Add note drag motion
665 GdkModifierType state;
666 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
671 if (_mouse_state == AddDragging) {
672 event_x = editor.frame_to_pixel(event_frame);
674 if (editor.snap_mode() == SnapNormal) {
675 /* event_frame will have been snapped to the start of the note we are under;
676 it's more intuitive if we use the end of that note here
678 event_x = editor.frame_to_pixel (event_frame + grid_frames);
680 event_x = editor.frame_to_pixel (event_frame);
687 if (event_x > _drag_start_x) {
688 _drag_rect->property_x2() = event_x;
691 _drag_rect->property_x1() = event_x;
695 if (_drag_rect && _mouse_state == SelectRectDragging) {
697 if (event_y > _drag_start_y) {
698 _drag_rect->property_y2() = event_y;
701 _drag_rect->property_y1() = event_y;
704 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y, Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
710 case SelectTouchDragging:
722 MidiRegionView::scroll (GdkEventScroll* ev)
724 if (_selection.empty()) {
728 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
729 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
730 it still works for zoom.
735 trackview.editor().verbose_cursor()->hide ();
737 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
739 if (ev->direction == GDK_SCROLL_UP) {
740 change_velocities (true, fine, false);
741 } else if (ev->direction == GDK_SCROLL_DOWN) {
742 change_velocities (false, fine, false);
748 MidiRegionView::key_press (GdkEventKey* ev)
750 /* since GTK bindings are generally activated on press, and since
751 detectable auto-repeat is the name of the game and only sends
752 repeated presses, carry out key actions at key press, not release.
755 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
757 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
758 _mouse_state = SelectTouchDragging;
761 } else if (ev->keyval == GDK_Escape && unmodified) {
765 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
767 bool start = (ev->keyval == GDK_comma);
768 bool end = (ev->keyval == GDK_period);
769 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
770 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
772 change_note_lengths (fine, shorter, 0.0, start, end);
776 } else if (ev->keyval == GDK_Delete && unmodified) {
781 } else if (ev->keyval == GDK_Tab) {
783 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
784 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
786 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
790 } else if (ev->keyval == GDK_ISO_Left_Tab) {
792 /* Shift-TAB generates ISO Left Tab, for some reason */
794 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
795 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
797 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
803 } else if (ev->keyval == GDK_Up) {
805 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
806 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
808 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
809 change_velocities (true, fine, allow_smush);
811 transpose (true, fine, allow_smush);
815 } else if (ev->keyval == GDK_Down) {
817 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
818 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
820 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
821 change_velocities (false, fine, allow_smush);
823 transpose (false, fine, allow_smush);
827 } else if (ev->keyval == GDK_Left && unmodified) {
832 } else if (ev->keyval == GDK_Right && unmodified) {
837 } else if (ev->keyval == GDK_c && unmodified) {
846 MidiRegionView::key_release (GdkEventKey* ev)
848 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
856 MidiRegionView::channel_edit ()
859 uint8_t current_channel = 0;
861 if (_selection.empty()) {
865 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
867 current_channel = (*i)->note()->channel ();
872 MidiChannelDialog channel_dialog (current_channel);
873 int ret = channel_dialog.run ();
876 case Gtk::RESPONSE_OK:
882 uint8_t new_channel = channel_dialog.active_channel ();
884 start_note_diff_command (_("channel edit"));
886 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
887 Selection::iterator next = i;
889 change_note_channel (*i, new_channel);
897 MidiRegionView::show_list_editor ()
900 _list_editor = new MidiListEditor (trackview.session(), midi_region());
902 _list_editor->present ();
905 /** Add a note to the model, and the view, at a canvas (click) coordinate.
906 * \param x horizontal position in pixels
907 * \param y vertical position in pixels
908 * \param length duration of the note in beats, which will be snapped to the grid
909 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
910 * \param snap_x true to snap x to the grid, otherwise false.
913 MidiRegionView::create_note_at (double x, double y, double length, bool sh, bool snap_x)
915 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
916 MidiStreamView* const view = mtv->midi_view();
918 double note = view->y_to_note(y);
921 assert(note <= 127.0);
923 // Start of note in frames relative to region start
924 framepos_t start_frames = trackview.editor().pixel_to_frame (x);
926 framecnt_t grid_frames;
927 start_frames = snap_frame_to_grid_underneath (start_frames, grid_frames);
929 assert(start_frames >= 0);
932 length = region_frames_to_region_beats(
933 snap_frame_to_frame (start_frames + region_beats_to_region_frames(length)) - start_frames);
935 assert (length != 0);
938 length = region_frames_to_region_beats (region_beats_to_region_frames (length) - 1);
941 const boost::shared_ptr<NoteType> new_note (new NoteType (mtv->get_channel_for_add (),
942 region_frames_to_region_beats(start_frames + _region->start()), length,
943 (uint8_t)note, 0x40));
945 if (_model->contains (new_note)) {
949 view->update_note_range(new_note->note());
951 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note");
953 _model->apply_command(*trackview.session(), cmd);
955 play_midi_note (new_note);
959 MidiRegionView::clear_events()
964 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
965 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
970 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
975 _patch_changes.clear();
977 _optimization_iterator = _events.end();
981 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
985 content_connection.disconnect ();
986 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
990 if (_enable_display) {
996 MidiRegionView::start_note_diff_command (string name)
998 if (!_note_diff_command) {
999 _note_diff_command = _model->new_note_diff_command (name);
1004 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1006 if (_note_diff_command) {
1007 _note_diff_command->add (note);
1010 _marked_for_selection.insert(note);
1012 if (show_velocity) {
1013 _marked_for_velocity.insert(note);
1018 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
1020 if (_note_diff_command && ev->note()) {
1021 _note_diff_command->remove(ev->note());
1026 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1027 MidiModel::NoteDiffCommand::Property property,
1030 if (_note_diff_command) {
1031 _note_diff_command->change (ev->note(), property, val);
1036 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1037 MidiModel::NoteDiffCommand::Property property,
1038 Evoral::MusicalTime val)
1040 if (_note_diff_command) {
1041 _note_diff_command->change (ev->note(), property, val);
1046 MidiRegionView::apply_diff (bool as_subcommand)
1050 if (!_note_diff_command) {
1054 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1055 // Mark all selected notes for selection when model reloads
1056 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1057 _marked_for_selection.insert((*i)->note());
1061 if (as_subcommand) {
1062 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1064 _model->apply_command (*trackview.session(), _note_diff_command);
1067 _note_diff_command = 0;
1068 midi_view()->midi_track()->playlist_modified();
1070 if (add_or_remove) {
1071 _marked_for_selection.clear();
1074 _marked_for_velocity.clear();
1078 MidiRegionView::abort_command()
1080 delete _note_diff_command;
1081 _note_diff_command = 0;
1086 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1088 if (_optimization_iterator != _events.end()) {
1089 ++_optimization_iterator;
1092 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1093 return *_optimization_iterator;
1096 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1097 if ((*_optimization_iterator)->note() == note) {
1098 return *_optimization_iterator;
1106 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1108 MidiModel::Notes notes;
1109 _model->get_notes (notes, op, val, chan_mask);
1111 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1112 CanvasNoteEvent* cne = find_canvas_note (*n);
1120 MidiRegionView::redisplay_model()
1122 // Don't redisplay the model if we're currently recording and displaying that
1123 if (_active_notes) {
1131 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1132 (*i)->invalidate ();
1135 MidiModel::ReadLock lock(_model->read_lock());
1137 MidiModel::Notes& notes (_model->notes());
1138 _optimization_iterator = _events.begin();
1140 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1142 boost::shared_ptr<NoteType> note (*n);
1143 CanvasNoteEvent* cne;
1146 if (note_in_region_range (note, visible)) {
1148 if ((cne = find_canvas_note (note)) != 0) {
1155 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1157 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1169 add_note (note, visible);
1174 if ((cne = find_canvas_note (note)) != 0) {
1182 /* remove note items that are no longer valid */
1184 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1185 if (!(*i)->valid ()) {
1187 i = _events.erase (i);
1193 _patch_changes.clear();
1197 display_patch_changes ();
1199 _marked_for_selection.clear ();
1200 _marked_for_velocity.clear ();
1202 /* we may have caused _events to contain things out of order (e.g. if a note
1203 moved earlier or later). we don't generally need them in time order, but
1204 make a note that a sort is required for those cases that require it.
1207 _sort_needed = true;
1211 MidiRegionView::display_patch_changes ()
1213 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1214 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1216 for (uint8_t i = 0; i < 16; ++i) {
1217 if (chn_mask & (1<<i)) {
1218 display_patch_changes_on_channel (i);
1220 /* TODO gray-out patch instad of not displaying it */
1225 MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
1227 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1229 if ((*i)->channel() != channel) {
1233 MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
1235 boost::shared_ptr<MIDI::Name::Patch> patch =
1236 MIDI::Name::MidiPatchManager::instance().find_patch(
1237 _model_name, _custom_device_mode, channel, patch_key);
1240 add_canvas_patch_change (*i, patch->name());
1243 /* program and bank numbers are zero-based: convert to one-based: MIDI_BP_ZERO */
1244 snprintf (buf, 16, "%d %d", (*i)->program() + MIDI_BP_ZERO , (*i)->bank() + MIDI_BP_ZERO);
1245 add_canvas_patch_change (*i, buf);
1251 MidiRegionView::display_sysexes()
1253 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1254 Evoral::MusicalTime time = (*i)->time();
1259 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1260 str << int((*i)->buffer()[b]);
1261 if (b != (*i)->size() -1) {
1265 string text = str.str();
1267 const double x = trackview.editor().frame_to_pixel(source_beats_to_absolute_frames(time));
1269 double height = midi_stream_view()->contents_height();
1271 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1272 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1274 // Show unless patch change is beyond the region bounds
1275 if (time - _region->start() >= _region->length() || time < _region->start()) {
1281 _sys_exes.push_back(sysex);
1286 MidiRegionView::~MidiRegionView ()
1288 in_destructor = true;
1290 trackview.editor().verbose_cursor()->hide ();
1292 note_delete_connection.disconnect ();
1294 delete _list_editor;
1296 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1298 if (_active_notes) {
1306 delete _note_diff_command;
1307 delete _step_edit_cursor;
1308 delete _temporary_note_group;
1312 MidiRegionView::region_resized (const PropertyChange& what_changed)
1314 RegionView::region_resized(what_changed);
1316 if (what_changed.contains (ARDOUR::Properties::position)) {
1317 set_duration(_region->length(), 0);
1318 if (_enable_display) {
1325 MidiRegionView::reset_width_dependent_items (double pixel_width)
1327 RegionView::reset_width_dependent_items(pixel_width);
1328 assert(_pixel_width == pixel_width);
1330 if (_enable_display) {
1334 move_step_edit_cursor (_step_edit_cursor_position);
1335 set_step_edit_cursor_width (_step_edit_cursor_width);
1339 MidiRegionView::set_height (double height)
1341 static const double FUDGE = 2.0;
1342 const double old_height = _height;
1343 RegionView::set_height(height);
1344 _height = height - FUDGE;
1346 apply_note_range(midi_stream_view()->lowest_note(),
1347 midi_stream_view()->highest_note(),
1348 height != old_height + FUDGE);
1351 name_pixbuf->raise_to_top();
1354 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1355 (*x)->set_height (midi_stream_view()->contents_height());
1358 if (_step_edit_cursor) {
1359 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1364 /** Apply the current note range from the stream view
1365 * by repositioning/hiding notes as necessary
1368 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1370 if (!_enable_display) {
1374 if (!force && _current_range_min == min && _current_range_max == max) {
1378 _current_range_min = min;
1379 _current_range_max = max;
1381 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1382 CanvasNoteEvent* event = *i;
1383 boost::shared_ptr<NoteType> note (event->note());
1385 if (note->note() < _current_range_min ||
1386 note->note() > _current_range_max) {
1392 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1394 const double y1 = midi_stream_view()->note_to_y(note->note());
1395 const double y2 = y1 + floor(midi_stream_view()->note_height());
1397 cnote->property_y1() = y1;
1398 cnote->property_y2() = y2;
1400 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1402 const double diamond_size = update_hit (chit);
1404 chit->set_height (diamond_size);
1410 MidiRegionView::add_ghost (TimeAxisView& tv)
1414 double unit_position = _region->position () / samples_per_unit;
1415 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1416 MidiGhostRegion* ghost;
1418 if (mtv && mtv->midi_view()) {
1419 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1420 to allow having midi notes on top of note lines and waveforms.
1422 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1424 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1427 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1428 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1429 ghost->add_note(note);
1433 ghost->set_height ();
1434 ghost->set_duration (_region->length() / samples_per_unit);
1435 ghosts.push_back (ghost);
1437 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1443 /** Begin tracking note state for successive calls to add_event
1446 MidiRegionView::begin_write()
1448 assert(!_active_notes);
1449 _active_notes = new CanvasNote*[128];
1450 for (unsigned i=0; i < 128; ++i) {
1451 _active_notes[i] = 0;
1456 /** Destroy note state for add_event
1459 MidiRegionView::end_write()
1461 delete[] _active_notes;
1463 _marked_for_selection.clear();
1464 _marked_for_velocity.clear();
1468 /** Resolve an active MIDI note (while recording).
1471 MidiRegionView::resolve_note(uint8_t note, double end_time)
1473 if (midi_view()->note_mode() != Sustained) {
1477 if (_active_notes && _active_notes[note]) {
1479 /* XXX is end_time really region-centric? I think so, because
1480 this is a new region that we're recording, so source zero is
1481 the same as region zero
1483 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1485 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1486 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1487 _active_notes[note] = 0;
1492 /** Extend active notes to rightmost edge of region (if length is changed)
1495 MidiRegionView::extend_active_notes()
1497 if (!_active_notes) {
1501 for (unsigned i=0; i < 128; ++i) {
1502 if (_active_notes[i]) {
1503 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1510 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1512 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1516 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1518 if (!route_ui || !route_ui->midi_track()) {
1522 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1528 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1530 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1534 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1536 if (!route_ui || !route_ui->midi_track()) {
1540 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1542 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1551 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1553 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1554 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1556 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1557 (note->note() <= midi_stream_view()->highest_note());
1562 /** Update a canvas note's size from its model note.
1563 * @param ev Canvas note to update.
1564 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1567 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1569 boost::shared_ptr<NoteType> note = ev->note();
1571 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time()));
1572 const double y1 = midi_stream_view()->note_to_y(note->note());
1574 ev->property_x1() = x;
1575 ev->property_y1() = y1;
1577 /* trim note display to not overlap the end of its region */
1579 if (note->length() > 0) {
1580 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1581 ev->property_x2() = trackview.editor().frame_to_pixel (note_end_frames);
1583 ev->property_x2() = trackview.editor().frame_to_pixel (_region->length());
1586 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1588 if (note->length() == 0) {
1589 if (_active_notes) {
1590 assert(note->note() < 128);
1591 // If this note is already active there's a stuck note,
1592 // finish the old note rectangle
1593 if (_active_notes[note->note()]) {
1594 CanvasNote* const old_rect = _active_notes[note->note()];
1595 boost::shared_ptr<NoteType> old_note = old_rect->note();
1596 old_rect->property_x2() = x;
1597 old_rect->property_outline_what() = (guint32) 0xF;
1599 _active_notes[note->note()] = ev;
1601 /* outline all but right edge */
1602 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1604 /* outline all edges */
1605 ev->property_outline_what() = (guint32) 0xF;
1608 if (update_ghost_regions) {
1609 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1610 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1612 gr->update_note (ev);
1619 MidiRegionView::update_hit (CanvasHit* ev)
1621 boost::shared_ptr<NoteType> note = ev->note();
1623 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1624 const double x = trackview.editor().frame_to_pixel(note_start_frames);
1625 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1626 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1630 return diamond_size;
1633 /** Add a MIDI note to the view (with length).
1635 * If in sustained mode, notes with length 0 will be considered active
1636 * notes, and resolve_note should be called when the corresponding note off
1637 * event arrives, to properly display the note.
1640 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1642 CanvasNoteEvent* event = 0;
1644 assert(note->time() >= 0);
1645 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1647 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1649 if (midi_view()->note_mode() == Sustained) {
1651 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1653 update_note (ev_rect);
1657 MidiGhostRegion* gr;
1659 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1660 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1661 gr->add_note(ev_rect);
1665 } else if (midi_view()->note_mode() == Percussive) {
1667 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1669 CanvasHit* ev_diamond = new CanvasHit (*this, *_note_group, diamond_size, note);
1671 update_hit (ev_diamond);
1680 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1681 note_selected(event, true);
1684 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1685 event->show_velocity();
1688 event->on_channel_selection_change(_last_channel_selection);
1689 _events.push_back(event);
1698 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1699 MidiStreamView* const view = mtv->midi_view();
1701 view->update_note_range (note->note());
1705 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1706 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1708 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1710 /* potentially extend region to hold new note */
1712 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1713 framepos_t region_end = _region->last_frame();
1715 if (end_frame > region_end) {
1716 _region->set_length (end_frame - _region->position());
1719 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1720 MidiStreamView* const view = mtv->midi_view();
1722 view->update_note_range(new_note->note());
1724 _marked_for_selection.clear ();
1727 start_note_diff_command (_("step add"));
1728 note_diff_add_note (new_note, true, false);
1731 // last_step_edit_note = new_note;
1735 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1737 change_note_lengths (false, false, beats, false, true);
1741 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
1743 assert (patch->time() >= 0);
1745 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (patch->time()));
1747 double const height = midi_stream_view()->contents_height();
1749 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1750 new CanvasPatchChange(*this, *_note_group,
1755 _custom_device_mode,
1759 // Show unless patch change is beyond the region bounds
1760 if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1761 patch_change->hide();
1763 patch_change->show();
1766 _patch_changes.push_back (patch_change);
1770 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1772 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1773 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1777 if (i != _model->patch_changes().end()) {
1778 key.msb = (*i)->bank_msb ();
1779 key.lsb = (*i)->bank_lsb ();
1780 key.program_number = (*i)->program ();
1782 key.msb = key.lsb = key.program_number = 0;
1785 assert (key.is_sane());
1790 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1792 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1794 if (pc.patch()->program() != new_patch.program_number) {
1795 c->change_program (pc.patch (), new_patch.program_number);
1798 int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1799 if (pc.patch()->bank() != new_bank) {
1800 c->change_bank (pc.patch (), new_bank);
1803 _model->apply_command (*trackview.session(), c);
1805 _patch_changes.clear ();
1806 display_patch_changes ();
1810 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1812 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1814 if (old_change->time() != new_change.time()) {
1815 c->change_time (old_change, new_change.time());
1818 if (old_change->channel() != new_change.channel()) {
1819 c->change_channel (old_change, new_change.channel());
1822 if (old_change->program() != new_change.program()) {
1823 c->change_program (old_change, new_change.program());
1826 if (old_change->bank() != new_change.bank()) {
1827 c->change_bank (old_change, new_change.bank());
1830 _model->apply_command (*trackview.session(), c);
1832 _patch_changes.clear ();
1833 display_patch_changes ();
1836 /** Add a patch change to the region.
1837 * @param t Time in frames relative to region position
1838 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1839 * MidiTimeAxisView::get_channel_for_add())
1842 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1844 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1846 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1847 c->add (MidiModel::PatchChangePtr (
1848 new Evoral::PatchChange<Evoral::MusicalTime> (
1849 absolute_frames_to_source_beats (_region->position() + t),
1850 mtv->get_channel_for_add(), patch.program(), patch.bank()
1855 _model->apply_command (*trackview.session(), c);
1857 _patch_changes.clear ();
1858 display_patch_changes ();
1862 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1864 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1865 c->change_time (pc.patch (), t);
1866 _model->apply_command (*trackview.session(), c);
1868 _patch_changes.clear ();
1869 display_patch_changes ();
1873 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1875 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1876 c->remove (pc->patch ());
1877 _model->apply_command (*trackview.session(), c);
1879 _patch_changes.clear ();
1880 display_patch_changes ();
1884 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1886 if (patch.patch()->program() < 127) {
1887 MIDI::Name::PatchPrimaryKey key;
1888 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1889 key.program_number++;
1890 change_patch_change (patch, key);
1895 MidiRegionView::next_patch (CanvasPatchChange& patch)
1897 if (patch.patch()->program() > 0) {
1898 MIDI::Name::PatchPrimaryKey key;
1899 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1900 key.program_number--;
1901 change_patch_change (patch, key);
1906 MidiRegionView::previous_bank (CanvasPatchChange& patch)
1908 if (patch.patch()->program() < 127) {
1909 MIDI::Name::PatchPrimaryKey key;
1910 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1913 change_patch_change (patch, key);
1918 change_patch_change (patch, key);
1925 MidiRegionView::next_bank (CanvasPatchChange& patch)
1927 if (patch.patch()->program() > 0) {
1928 MIDI::Name::PatchPrimaryKey key;
1929 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1930 if (key.lsb < 127) {
1932 change_patch_change (patch, key);
1934 if (key.msb < 127) {
1937 change_patch_change (patch, key);
1944 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1946 if (_selection.empty()) {
1950 _selection.erase (cne);
1954 MidiRegionView::delete_selection()
1956 if (_selection.empty()) {
1960 start_note_diff_command (_("delete selection"));
1962 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1963 if ((*i)->selected()) {
1964 _note_diff_command->remove((*i)->note());
1974 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1976 start_note_diff_command (_("delete note"));
1977 _note_diff_command->remove (n);
1980 trackview.editor().verbose_cursor()->hide ();
1984 MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal)
1986 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1988 Selection::iterator tmp = i;
1991 (*i)->set_selected (false);
1992 (*i)->hide_velocity ();
1993 _selection.erase (i);
2001 /* this does not change the status of this regionview w.r.t the editor
2006 SelectionCleared (this); /* EMIT SIGNAL */
2011 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
2013 clear_selection_except (ev);
2015 /* don't bother with checking to see if we should remove this
2016 regionview from the editor selection, since we're about to add
2017 another note, and thus put/keep this regionview in the editor
2021 if (!ev->selected()) {
2022 add_to_selection (ev);
2027 MidiRegionView::select_all_notes ()
2031 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2032 add_to_selection (*i);
2037 MidiRegionView::select_range (framepos_t start, framepos_t end)
2041 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2042 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2043 if (t >= start && t <= end) {
2044 add_to_selection (*i);
2050 MidiRegionView::invert_selection ()
2052 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2053 if ((*i)->selected()) {
2054 remove_from_selection(*i);
2056 add_to_selection (*i);
2062 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2064 uint8_t low_note = 127;
2065 uint8_t high_note = 0;
2066 MidiModel::Notes& notes (_model->notes());
2067 _optimization_iterator = _events.begin();
2073 if (extend && _selection.empty()) {
2079 /* scan existing selection to get note range */
2081 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2082 if ((*i)->note()->note() < low_note) {
2083 low_note = (*i)->note()->note();
2085 if ((*i)->note()->note() > high_note) {
2086 high_note = (*i)->note()->note();
2090 low_note = min (low_note, notenum);
2091 high_note = max (high_note, notenum);
2094 _no_sound_notes = true;
2096 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2098 boost::shared_ptr<NoteType> note (*n);
2099 CanvasNoteEvent* cne;
2100 bool select = false;
2102 if (((1 << note->channel()) & channel_mask) != 0) {
2104 if ((note->note() >= low_note && note->note() <= high_note)) {
2107 } else if (note->note() == notenum) {
2113 if ((cne = find_canvas_note (note)) != 0) {
2114 // extend is false because we've taken care of it,
2115 // since it extends by time range, not pitch.
2116 note_selected (cne, add, false);
2120 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2124 _no_sound_notes = false;
2128 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2130 MidiModel::Notes& notes (_model->notes());
2131 _optimization_iterator = _events.begin();
2133 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2135 boost::shared_ptr<NoteType> note (*n);
2136 CanvasNoteEvent* cne;
2138 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2139 if ((cne = find_canvas_note (note)) != 0) {
2140 if (cne->selected()) {
2141 note_deselected (cne);
2143 note_selected (cne, true, false);
2151 MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2154 clear_selection_except (ev);
2155 if (!_selection.empty()) {
2156 PublicEditor& editor (trackview.editor());
2157 editor.get_selection().add (this);
2163 if (!ev->selected()) {
2164 add_to_selection (ev);
2168 /* find end of latest note selected, select all between that and the start of "ev" */
2170 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2171 Evoral::MusicalTime latest = 0;
2173 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2174 if ((*i)->note()->end_time() > latest) {
2175 latest = (*i)->note()->end_time();
2177 if ((*i)->note()->time() < earliest) {
2178 earliest = (*i)->note()->time();
2182 if (ev->note()->end_time() > latest) {
2183 latest = ev->note()->end_time();
2186 if (ev->note()->time() < earliest) {
2187 earliest = ev->note()->time();
2190 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2192 /* find notes entirely within OR spanning the earliest..latest range */
2194 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2195 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2196 add_to_selection (*i);
2204 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2206 remove_from_selection (ev);
2210 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
2220 // TODO: Make this faster by storing the last updated selection rect, and only
2221 // adjusting things that are in the area that appears/disappeared.
2222 // We probably need a tree to be able to find events in O(log(n)) time.
2224 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2226 /* check if any corner of the note is inside the rect
2229 1) this is computing "touched by", not "contained by" the rect.
2230 2) this does not require that events be sorted in time.
2233 const double ix1 = (*i)->x1();
2234 const double ix2 = (*i)->x2();
2235 const double iy1 = (*i)->y1();
2236 const double iy2 = (*i)->y2();
2238 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2239 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2240 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2241 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2244 if (!(*i)->selected()) {
2245 add_to_selection (*i);
2247 } else if ((*i)->selected() && !extend) {
2248 // Not inside rectangle
2249 remove_from_selection (*i);
2255 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2257 Selection::iterator i = _selection.find (ev);
2259 if (i != _selection.end()) {
2260 _selection.erase (i);
2263 ev->set_selected (false);
2264 ev->hide_velocity ();
2266 if (_selection.empty()) {
2267 PublicEditor& editor (trackview.editor());
2268 editor.get_selection().remove (this);
2273 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2275 bool add_mrv_selection = false;
2277 if (_selection.empty()) {
2278 add_mrv_selection = true;
2281 if (_selection.insert (ev).second) {
2282 ev->set_selected (true);
2283 play_midi_note ((ev)->note());
2286 if (add_mrv_selection) {
2287 PublicEditor& editor (trackview.editor());
2288 editor.get_selection().add (this);
2293 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2295 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2296 PossibleChord to_play;
2297 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2299 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2300 if ((*i)->note()->time() < earliest) {
2301 earliest = (*i)->note()->time();
2305 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2306 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2307 to_play.push_back ((*i)->note());
2309 (*i)->move_event(dx, dy);
2312 if (dy && !_selection.empty() && !_no_sound_notes && trackview.editor().sound_notes()) {
2314 if (to_play.size() > 1) {
2316 PossibleChord shifted;
2318 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2319 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2320 moved_note->set_note (moved_note->note() + cumulative_dy);
2321 shifted.push_back (moved_note);
2324 play_midi_chord (shifted);
2326 } else if (!to_play.empty()) {
2328 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2329 moved_note->set_note (moved_note->note() + cumulative_dy);
2330 play_midi_note (moved_note);
2336 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2338 assert (!_selection.empty());
2340 uint8_t lowest_note_in_selection = 127;
2341 uint8_t highest_note_in_selection = 0;
2342 uint8_t highest_note_difference = 0;
2344 // find highest and lowest notes first
2346 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2347 uint8_t pitch = (*i)->note()->note();
2348 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2349 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2353 cerr << "dnote: " << (int) dnote << endl;
2354 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2355 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2356 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2357 << int(highest_note_in_selection) << endl;
2358 cerr << "selection size: " << _selection.size() << endl;
2359 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2362 // Make sure the note pitch does not exceed the MIDI standard range
2363 if (highest_note_in_selection + dnote > 127) {
2364 highest_note_difference = highest_note_in_selection - 127;
2367 start_note_diff_command (_("move notes"));
2369 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2371 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (source_beats_to_absolute_frames ((*i)->note()->time()) + dt);
2377 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2379 uint8_t original_pitch = (*i)->note()->note();
2380 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2382 // keep notes in standard midi range
2383 clamp_to_0_127(new_pitch);
2385 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2386 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2388 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2393 // care about notes being moved beyond the upper/lower bounds on the canvas
2394 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2395 highest_note_in_selection > midi_stream_view()->highest_note()) {
2396 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2400 /** @param x Pixel relative to the region position.
2401 * @return Snapped frame relative to the region position.
2404 MidiRegionView::snap_pixel_to_frame(double x)
2406 PublicEditor& editor (trackview.editor());
2407 return snap_frame_to_frame (editor.pixel_to_frame (x));
2410 /** @param x Pixel relative to the region position.
2411 * @return Snapped pixel relative to the region position.
2414 MidiRegionView::snap_to_pixel(double x)
2416 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2420 MidiRegionView::get_position_pixels()
2422 framepos_t region_frame = get_position();
2423 return trackview.editor().frame_to_pixel(region_frame);
2427 MidiRegionView::get_end_position_pixels()
2429 framepos_t frame = get_position() + get_duration ();
2430 return trackview.editor().frame_to_pixel(frame);
2434 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2436 /* the time converter will return the frame corresponding to `beats'
2437 relative to the start of the source. The start of the source
2438 is an implied position given by region->position - region->start
2440 const framepos_t source_start = _region->position() - _region->start();
2441 return source_start + _source_relative_time_converter.to (beats);
2445 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2447 /* the `frames' argument needs to be converted into a frame count
2448 relative to the start of the source before being passed in to the
2451 const framepos_t source_start = _region->position() - _region->start();
2452 return _source_relative_time_converter.from (frames - source_start);
2456 MidiRegionView::region_beats_to_region_frames(double beats) const
2458 return _region_relative_time_converter.to(beats);
2462 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2464 return _region_relative_time_converter.from(frames);
2468 MidiRegionView::begin_resizing (bool /*at_front*/)
2470 _resize_data.clear();
2472 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2473 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2475 // only insert CanvasNotes into the map
2477 NoteResizeData *resize_data = new NoteResizeData();
2478 resize_data->canvas_note = note;
2480 // create a new SimpleRect from the note which will be the resize preview
2481 SimpleRect *resize_rect = new SimpleRect(
2482 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2484 // calculate the colors: get the color settings
2485 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2486 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2489 // make the resize preview notes more transparent and bright
2490 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2492 // calculate color based on note velocity
2493 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2494 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2498 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2499 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2501 resize_data->resize_rect = resize_rect;
2502 _resize_data.push_back(resize_data);
2507 /** Update resizing notes while user drags.
2508 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2509 * @param at_front which end of the note (true == note on, false == note off)
2510 * @param delta_x change in mouse position since the start of the drag
2511 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2512 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2513 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2514 * as the \a primary note.
2517 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2519 bool cursor_set = false;
2521 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2522 SimpleRect* resize_rect = (*i)->resize_rect;
2523 CanvasNote* canvas_note = (*i)->canvas_note;
2528 current_x = canvas_note->x1() + delta_x;
2530 current_x = primary->x1() + delta_x;
2534 current_x = canvas_note->x2() + delta_x;
2536 current_x = primary->x2() + delta_x;
2541 resize_rect->property_x1() = snap_to_pixel(current_x);
2542 resize_rect->property_x2() = canvas_note->x2();
2544 resize_rect->property_x2() = snap_to_pixel(current_x);
2545 resize_rect->property_x1() = canvas_note->x1();
2551 beats = snap_pixel_to_frame (current_x);
2552 beats = region_frames_to_region_beats (beats);
2557 if (beats < canvas_note->note()->end_time()) {
2558 len = canvas_note->note()->time() - beats;
2559 len += canvas_note->note()->length();
2564 if (beats >= canvas_note->note()->time()) {
2565 len = beats - canvas_note->note()->time();
2572 snprintf (buf, sizeof (buf), "%.3g beats", len);
2573 show_verbose_cursor (buf, 0, 0);
2582 /** Finish resizing notes when the user releases the mouse button.
2583 * Parameters the same as for \a update_resizing().
2586 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2588 start_note_diff_command (_("resize notes"));
2590 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2591 CanvasNote* canvas_note = (*i)->canvas_note;
2592 SimpleRect* resize_rect = (*i)->resize_rect;
2594 /* Get the new x position for this resize, which is in pixels relative
2595 * to the region position.
2602 current_x = canvas_note->x1() + delta_x;
2604 current_x = primary->x1() + delta_x;
2608 current_x = canvas_note->x2() + delta_x;
2610 current_x = primary->x2() + delta_x;
2614 /* Convert that to a frame within the source */
2615 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2617 /* and then to beats */
2618 current_x = region_frames_to_region_beats (current_x);
2620 if (at_front && current_x < canvas_note->note()->end_time()) {
2621 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2623 double len = canvas_note->note()->time() - current_x;
2624 len += canvas_note->note()->length();
2627 /* XXX convert to beats */
2628 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2633 double len = current_x - canvas_note->note()->time();
2636 /* XXX convert to beats */
2637 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2645 _resize_data.clear();
2650 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2652 uint8_t new_velocity;
2655 new_velocity = event->note()->velocity() + velocity;
2656 clamp_to_0_127(new_velocity);
2658 new_velocity = velocity;
2661 event->set_selected (event->selected()); // change color
2663 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2667 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2672 new_note = event->note()->note() + note;
2677 clamp_to_0_127 (new_note);
2678 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2682 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2684 bool change_start = false;
2685 bool change_length = false;
2686 Evoral::MusicalTime new_start = 0;
2687 Evoral::MusicalTime new_length = 0;
2689 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2691 front_delta: if positive - move the start of the note later in time (shortening it)
2692 if negative - move the start of the note earlier in time (lengthening it)
2694 end_delta: if positive - move the end of the note later in time (lengthening it)
2695 if negative - move the end of the note earlier in time (shortening it)
2699 if (front_delta < 0) {
2701 if (event->note()->time() < -front_delta) {
2704 new_start = event->note()->time() + front_delta; // moves earlier
2707 /* start moved toward zero, so move the end point out to where it used to be.
2708 Note that front_delta is negative, so this increases the length.
2711 new_length = event->note()->length() - front_delta;
2712 change_start = true;
2713 change_length = true;
2717 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2719 if (new_pos < event->note()->end_time()) {
2720 new_start = event->note()->time() + front_delta;
2721 /* start moved toward the end, so move the end point back to where it used to be */
2722 new_length = event->note()->length() - front_delta;
2723 change_start = true;
2724 change_length = true;
2731 bool can_change = true;
2732 if (end_delta < 0) {
2733 if (event->note()->length() < -end_delta) {
2739 new_length = event->note()->length() + end_delta;
2740 change_length = true;
2745 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2748 if (change_length) {
2749 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2754 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2756 uint8_t new_channel;
2760 if (event->note()->channel() < -chn) {
2763 new_channel = event->note()->channel() + chn;
2766 new_channel = event->note()->channel() + chn;
2769 new_channel = (uint8_t) chn;
2772 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2776 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2778 Evoral::MusicalTime new_time;
2782 if (event->note()->time() < -delta) {
2785 new_time = event->note()->time() + delta;
2788 new_time = event->note()->time() + delta;
2794 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2798 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2800 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2804 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2808 if (_selection.empty()) {
2823 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2824 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2830 start_note_diff_command (_("change velocities"));
2832 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2833 Selection::iterator next = i;
2835 change_note_velocity (*i, delta, true);
2841 if (!_selection.empty()) {
2843 snprintf (buf, sizeof (buf), "Vel %d",
2844 (int) (*_selection.begin())->note()->velocity());
2845 show_verbose_cursor (buf, 10, 10);
2851 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2853 if (_selection.empty()) {
2870 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2872 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2876 if ((int8_t) (*i)->note()->note() + delta > 127) {
2883 start_note_diff_command (_("transpose"));
2885 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2886 Selection::iterator next = i;
2888 change_note_note (*i, delta, true);
2896 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2902 /* grab the current grid distance */
2904 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2906 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2907 cerr << "Grid type not available as beats - TO BE FIXED\n";
2917 start_note_diff_command (_("change note lengths"));
2919 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2920 Selection::iterator next = i;
2923 /* note the negation of the delta for start */
2925 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2934 MidiRegionView::nudge_notes (bool forward)
2936 if (_selection.empty()) {
2940 /* pick a note as the point along the timeline to get the nudge distance.
2941 its not necessarily the earliest note, so we may want to pull the notes out
2942 into a vector and sort before using the first one.
2945 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
2947 framepos_t distance;
2949 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2951 /* grid is off - use nudge distance */
2953 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2959 framepos_t next_pos = ref_point;
2962 if (max_framepos - 1 < next_pos) {
2966 if (next_pos == 0) {
2972 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2973 distance = ref_point - next_pos;
2976 if (distance == 0) {
2980 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
2986 start_note_diff_command (_("nudge"));
2988 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2989 Selection::iterator next = i;
2991 change_note_time (*i, delta, true);
2999 MidiRegionView::change_channel(uint8_t channel)
3001 start_note_diff_command(_("change channel"));
3002 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3003 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3011 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
3013 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3015 _pre_enter_cursor = editor->get_canvas_cursor ();
3017 if (_mouse_state == SelectTouchDragging) {
3018 note_selected (ev, true);
3021 show_verbose_cursor (ev->note ());
3025 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
3027 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3029 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3030 (*i)->hide_velocity ();
3033 editor->verbose_cursor()->hide ();
3035 if (_pre_enter_cursor) {
3036 editor->set_canvas_cursor (_pre_enter_cursor);
3037 _pre_enter_cursor = 0;
3042 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
3045 /* XXX should get patch name if we can */
3046 s << _("Bank:") << (ev->patch()->bank() + MIDI_BP_ZERO) << '\n' << _("Program:") << ((int) ev->patch()->program()) + MIDI_BP_ZERO << '\n' << _("Channel:") << ((int) ev->patch()->channel() + 1);
3047 show_verbose_cursor (s.str(), 10, 20);
3051 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
3053 trackview.editor().verbose_cursor()->hide ();
3057 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3059 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3061 if (x_fraction > 0.0 && x_fraction < 0.25) {
3062 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3063 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
3064 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3066 if (_pre_enter_cursor && can_set_cursor) {
3067 editor->set_canvas_cursor (_pre_enter_cursor);
3073 MidiRegionView::set_frame_color()
3077 TimeAxisViewItem::set_frame_color ();
3084 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3085 } else if (high_enough_for_name) {
3086 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3091 if (!rect_visible) {
3092 f = UINT_RGBA_CHANGE_A (f, 0);
3095 frame->property_fill_color_rgba() = f;
3099 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3103 case FilterChannels:
3104 _force_channel = -1;
3107 _force_channel = mask;
3108 mask = 0xFFFF; // Show all notes as active (below)
3111 // Update notes for selection
3112 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3113 (*i)->on_channel_selection_change(mask);
3116 _last_channel_selection = mask;
3118 _patch_changes.clear ();
3119 display_patch_changes ();
3123 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
3125 _model_name = model;
3126 _custom_device_mode = custom_device_mode;
3131 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3133 if (_selection.empty()) {
3137 PublicEditor& editor (trackview.editor());
3141 /* XXX what to do ? */
3145 editor.get_cut_buffer().add (selection_as_cut_buffer());
3153 start_note_diff_command();
3155 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3162 note_diff_remove_note (*i);
3172 MidiRegionView::selection_as_cut_buffer () const
3176 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3177 NoteType* n = (*i)->note().get();
3178 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3181 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3187 /** This method handles undo */
3189 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3195 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3197 trackview.session()->begin_reversible_command (_("paste"));
3199 start_note_diff_command (_("paste"));
3201 Evoral::MusicalTime beat_delta;
3202 Evoral::MusicalTime paste_pos_beats;
3203 Evoral::MusicalTime duration;
3204 Evoral::MusicalTime end_point = 0;
3206 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3207 paste_pos_beats = region_frames_to_region_beats (pos - _region->position());
3208 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3209 paste_pos_beats = 0;
3211 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6 ; beat delta = %7\n",
3212 (*mcb.notes().begin())->time(),
3213 (*mcb.notes().rbegin())->end_time(),
3214 duration, pos, _region->position(),
3215 paste_pos_beats, beat_delta));
3219 for (int n = 0; n < (int) times; ++n) {
3221 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3223 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3224 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3226 /* make all newly added notes selected */
3228 note_diff_add_note (copied_note, true);
3229 end_point = copied_note->end_time();
3232 paste_pos_beats += duration;
3235 /* if we pasted past the current end of the region, extend the region */
3237 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3238 framepos_t region_end = _region->position() + _region->length() - 1;
3240 if (end_frame > region_end) {
3242 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3244 _region->clear_changes ();
3245 _region->set_length (end_frame);
3246 trackview.session()->add_command (new StatefulDiffCommand (_region));
3251 trackview.session()->commit_reversible_command ();
3254 struct EventNoteTimeEarlyFirstComparator {
3255 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3256 return a->note()->time() < b->note()->time();
3261 MidiRegionView::time_sort_events ()
3263 if (!_sort_needed) {
3267 EventNoteTimeEarlyFirstComparator cmp;
3270 _sort_needed = false;
3274 MidiRegionView::goto_next_note (bool add_to_selection)
3276 bool use_next = false;
3278 if (_events.back()->selected()) {
3282 time_sort_events ();
3284 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3285 if ((*i)->selected()) {
3288 } else if (use_next) {
3289 if (!add_to_selection) {
3292 note_selected (*i, true, false);
3298 /* use the first one */
3300 unique_select (_events.front());
3305 MidiRegionView::goto_previous_note (bool add_to_selection)
3307 bool use_next = false;
3309 if (_events.front()->selected()) {
3313 time_sort_events ();
3315 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3316 if ((*i)->selected()) {
3319 } else if (use_next) {
3320 if (!add_to_selection) {
3323 note_selected (*i, true, false);
3329 /* use the last one */
3331 unique_select (*(_events.rbegin()));
3335 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3337 bool had_selected = false;
3339 time_sort_events ();
3341 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3342 if ((*i)->selected()) {
3343 selected.insert ((*i)->note());
3344 had_selected = true;
3348 if (allow_all_if_none_selected && !had_selected) {
3349 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3350 selected.insert ((*i)->note());
3356 MidiRegionView::update_ghost_note (double x, double y)
3358 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3363 _note_group->w2i (x, y);
3365 PublicEditor& editor = trackview.editor ();
3367 framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3368 framecnt_t grid_frames;
3369 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3371 /* use region_frames... because we are converting a delta within the region
3374 double length = region_frames_to_region_beats (snap_frame_to_frame (f + grid_frames) - f);
3376 /* note that this sets the time of the ghost note in beats relative to
3377 the start of the source; that is how all note times are stored.
3379 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3380 _ghost_note->note()->set_length (length);
3381 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3382 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3384 /* the ghost note does not appear in ghost regions, so pass false in here */
3385 update_note (_ghost_note, false);
3387 show_verbose_cursor (_ghost_note->note ());
3391 MidiRegionView::create_ghost_note (double x, double y)
3396 boost::shared_ptr<NoteType> g (new NoteType);
3397 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3398 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3399 update_ghost_note (x, y);
3400 _ghost_note->show ();
3405 show_verbose_cursor (_ghost_note->note ());
3409 MidiRegionView::snap_changed ()
3415 create_ghost_note (_last_ghost_x, _last_ghost_y);
3419 MidiRegionView::drop_down_keys ()
3421 _mouse_state = None;
3425 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3427 double note = midi_stream_view()->y_to_note(y);
3429 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3431 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3433 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3434 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3435 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3436 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3441 bool add_mrv_selection = false;
3443 if (_selection.empty()) {
3444 add_mrv_selection = true;
3447 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3448 if (_selection.insert (*i).second) {
3449 (*i)->set_selected (true);
3453 if (add_mrv_selection) {
3454 PublicEditor& editor (trackview.editor());
3455 editor.get_selection().add (this);
3460 MidiRegionView::color_handler ()
3462 RegionView::color_handler ();
3464 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3465 (*i)->set_selected ((*i)->selected()); // will change color
3468 /* XXX probably more to do here */
3472 MidiRegionView::enable_display (bool yn)
3474 RegionView::enable_display (yn);
3481 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3483 if (_step_edit_cursor == 0) {
3484 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3486 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3487 _step_edit_cursor->property_y1() = 0;
3488 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3489 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3490 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3493 move_step_edit_cursor (pos);
3494 _step_edit_cursor->show ();
3498 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3500 _step_edit_cursor_position = pos;
3502 if (_step_edit_cursor) {
3503 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3504 _step_edit_cursor->property_x1() = pixel;
3505 set_step_edit_cursor_width (_step_edit_cursor_width);
3510 MidiRegionView::hide_step_edit_cursor ()
3512 if (_step_edit_cursor) {
3513 _step_edit_cursor->hide ();
3518 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3520 _step_edit_cursor_width = beats;
3522 if (_step_edit_cursor) {
3523 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3527 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3528 * @param w Source that the data will end up in.
3531 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3533 if (!_active_notes) {
3534 /* we aren't actively being recorded to */
3538 boost::shared_ptr<MidiSource> src = w.lock ();
3539 if (!src || src != midi_region()->midi_source()) {
3540 /* recorded data was not destined for our source */
3544 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3546 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3548 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3550 framepos_t back = max_framepos;
3552 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3553 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3554 assert (ev.buffer ());
3556 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3558 if (ev.type() == MIDI_CMD_NOTE_ON) {
3560 boost::shared_ptr<NoteType> note (
3561 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3564 add_note (note, true);
3566 /* fix up our note range */
3567 if (ev.note() < _current_range_min) {
3568 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3569 } else if (ev.note() > _current_range_max) {
3570 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3573 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3574 resolve_note (ev.note (), time_beats);
3580 midi_stream_view()->check_record_layers (region(), back);
3584 MidiRegionView::trim_front_starting ()
3586 /* Reparent the note group to the region view's parent, so that it doesn't change
3587 when the region view is trimmed.
3589 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3590 _temporary_note_group->move (group->property_x(), group->property_y());
3591 _note_group->reparent (*_temporary_note_group);
3595 MidiRegionView::trim_front_ending ()
3597 _note_group->reparent (*group);
3598 delete _temporary_note_group;
3599 _temporary_note_group = 0;
3601 if (_region->start() < 0) {
3602 /* Trim drag made start time -ve; fix this */
3603 midi_region()->fix_negative_start ();
3608 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3610 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3611 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3615 change_patch_change (pc->patch(), d.patch ());
3620 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3623 snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3624 Evoral::midi_note_name (n->note()).c_str(),
3626 (int) n->channel() + 1,
3627 (int) n->velocity());
3629 show_verbose_cursor (buf, 10, 20);
3633 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3637 trackview.editor().get_pointer_position (wx, wy);
3642 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3644 double x1, y1, x2, y2;
3645 trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3647 if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3648 wy -= (y2 - y1) + 2 * yoffset;
3651 trackview.editor().verbose_cursor()->set (text, wx, wy);
3652 trackview.editor().verbose_cursor()->show ();
3655 /** @param p A session framepos.
3656 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3657 * @return p snapped to the grid subdivision underneath it.
3660 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3662 PublicEditor& editor = trackview.editor ();
3665 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3671 grid_frames = region_beats_to_region_frames (grid_beats);
3673 /* Hack so that we always snap to the note that we are over, instead of snapping
3674 to the next one if we're more than halfway through the one we're over.
3676 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3677 p -= grid_frames / 2;
3680 return snap_frame_to_frame (p);
3683 /** Called when the selection has been cleared in any MidiRegionView.
3684 * @param rv MidiRegionView that the selection was cleared in.
3687 MidiRegionView::selection_cleared (MidiRegionView* rv)
3693 /* Clear our selection in sympathy; but don't signal the fact */
3694 clear_selection (false);