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_cut_buffer.h"
58 #include "midi_list_editor.h"
59 #include "midi_region_view.h"
60 #include "midi_streamview.h"
61 #include "midi_time_axis.h"
62 #include "midi_util.h"
63 #include "note_player.h"
64 #include "public_editor.h"
65 #include "rgb_macros.h"
66 #include "selection.h"
67 #include "simpleline.h"
68 #include "streamview.h"
70 #include "mouse_cursors.h"
71 #include "patch_change_dialog.h"
72 #include "verbose_cursor.h"
76 using namespace ARDOUR;
78 using namespace Editing;
79 using namespace ArdourCanvas;
80 using Gtkmm2ext::Keyboard;
82 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
83 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
84 : RegionView (parent, tv, r, spu, basic_color)
86 , _last_channel_selection(0xFFFF)
87 , _current_range_min(0)
88 , _current_range_max(0)
89 , _model_name(string())
90 , _custom_device_mode(string())
92 , _note_group(new ArdourCanvas::Group(*group))
93 , _note_diff_command (0)
96 , _step_edit_cursor (0)
97 , _step_edit_cursor_width (1.0)
98 , _step_edit_cursor_position (0.0)
99 , _channel_selection_scoped_note (0)
100 , _temporary_note_group (0)
103 , _sort_needed (true)
104 , _optimization_iterator (_events.end())
106 , _no_sound_notes (false)
109 , _pre_enter_cursor (0)
111 _note_group->raise_to_top();
112 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
114 connect_to_diskstream ();
117 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
118 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
119 TimeAxisViewItem::Visibility visibility)
120 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
122 , _last_channel_selection(0xFFFF)
123 , _model_name(string())
124 , _custom_device_mode(string())
126 , _note_group(new ArdourCanvas::Group(*parent))
127 , _note_diff_command (0)
130 , _step_edit_cursor (0)
131 , _step_edit_cursor_width (1.0)
132 , _step_edit_cursor_position (0.0)
133 , _channel_selection_scoped_note (0)
134 , _temporary_note_group (0)
137 , _sort_needed (true)
138 , _optimization_iterator (_events.end())
140 , _no_sound_notes (false)
144 _note_group->raise_to_top();
145 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
147 connect_to_diskstream ();
150 MidiRegionView::MidiRegionView (const MidiRegionView& other)
151 : sigc::trackable(other)
154 , _last_channel_selection(0xFFFF)
155 , _model_name(string())
156 , _custom_device_mode(string())
158 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
159 , _note_diff_command (0)
162 , _step_edit_cursor (0)
163 , _step_edit_cursor_width (1.0)
164 , _step_edit_cursor_position (0.0)
165 , _channel_selection_scoped_note (0)
166 , _temporary_note_group (0)
169 , _sort_needed (true)
170 , _optimization_iterator (_events.end())
172 , _no_sound_notes (false)
179 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
180 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
185 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
186 : RegionView (other, boost::shared_ptr<Region> (region))
188 , _last_channel_selection(0xFFFF)
189 , _model_name(string())
190 , _custom_device_mode(string())
192 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
193 , _note_diff_command (0)
196 , _step_edit_cursor (0)
197 , _step_edit_cursor_width (1.0)
198 , _step_edit_cursor_position (0.0)
199 , _channel_selection_scoped_note (0)
200 , _temporary_note_group (0)
203 , _sort_needed (true)
204 , _optimization_iterator (_events.end())
206 , _no_sound_notes (false)
213 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
214 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
220 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
222 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
224 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
225 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
229 midi_region()->midi_source(0)->load_model();
232 _model = midi_region()->midi_source(0)->model();
233 _enable_display = false;
235 RegionView::init (basic_color, false);
237 compute_colors (basic_color);
239 set_height (trackview.current_height());
242 region_sync_changed ();
243 region_resized (ARDOUR::bounds_change);
246 reset_width_dependent_items (_pixel_width);
250 _enable_display = true;
253 display_model (_model);
257 group->raise_to_top();
258 group->signal_event().connect(
259 sigc::mem_fun(this, &MidiRegionView::canvas_event), false);
261 midi_view()->signal_channel_mode_changed().connect(
262 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
264 midi_view()->signal_midi_patch_settings_changed().connect(
265 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
267 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
268 ui_bind(&MidiRegionView::snap_changed, this),
271 connect_to_diskstream ();
275 MidiRegionView::connect_to_diskstream ()
277 midi_view()->midi_track()->DataRecorded.connect(
278 *this, invalidator(*this),
279 ui_bind(&MidiRegionView::data_recorded, this, _1, _2),
284 MidiRegionView::canvas_event(GdkEvent* ev)
287 case GDK_ENTER_NOTIFY:
288 case GDK_LEAVE_NOTIFY:
289 _last_event_x = ev->crossing.x;
290 _last_event_y = ev->crossing.y;
292 case GDK_MOTION_NOTIFY:
293 _last_event_x = ev->motion.x;
294 _last_event_y = ev->motion.y;
300 if (!trackview.editor().internal_editing()) {
306 return scroll (&ev->scroll);
309 return key_press (&ev->key);
311 case GDK_KEY_RELEASE:
312 return key_release (&ev->key);
314 case GDK_BUTTON_PRESS:
315 return button_press (&ev->button);
317 case GDK_2BUTTON_PRESS:
320 case GDK_BUTTON_RELEASE:
321 return button_release (&ev->button);
323 case GDK_ENTER_NOTIFY:
324 return enter_notify (&ev->crossing);
326 case GDK_LEAVE_NOTIFY:
327 return leave_notify (&ev->crossing);
329 case GDK_MOTION_NOTIFY:
330 return motion (&ev->motion);
340 MidiRegionView::remove_ghost_note ()
347 MidiRegionView::enter_notify (GdkEventCrossing* ev)
349 trackview.editor().MouseModeChanged.connect (
350 _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
353 Keyboard::magic_widget_grab_focus();
356 if (trackview.editor().current_mouse_mode() == MouseRange) {
357 create_ghost_note (ev->x, ev->y);
364 MidiRegionView::leave_notify (GdkEventCrossing*)
366 _mouse_mode_connection.disconnect ();
368 trackview.editor().verbose_cursor()->hide ();
369 remove_ghost_note ();
374 MidiRegionView::mouse_mode_changed ()
376 if (trackview.editor().current_mouse_mode() == MouseRange && trackview.editor().internal_editing()) {
377 create_ghost_note (_last_event_x, _last_event_y);
379 remove_ghost_note ();
380 trackview.editor().verbose_cursor()->hide ();
385 MidiRegionView::button_press (GdkEventButton* ev)
387 if (ev->button != 1) {
394 group->w2i (_last_x, _last_y);
396 if (_mouse_state != SelectTouchDragging) {
398 _pressed_button = ev->button;
399 _mouse_state = Pressed;
404 _pressed_button = ev->button;
410 MidiRegionView::button_release (GdkEventButton* ev)
412 double event_x, event_y;
413 framepos_t event_frame = 0;
415 if (ev->button != 1) {
422 group->w2i(event_x, event_y);
423 group->ungrab(ev->time);
425 event_frame = trackview.editor().pixel_to_frame(event_x);
427 switch (_mouse_state) {
428 case Pressed: // Clicked
430 switch (trackview.editor().current_mouse_mode()) {
436 if (Keyboard::is_insert_note_event(ev)){
438 double event_x, event_y;
442 group->w2i(event_x, event_y);
445 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
451 create_note_at (event_x, event_y, beats, true);
459 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
465 create_note_at (event_x, event_y, beats, true);
476 case SelectRectDragging: // Select drag done
483 case AddDragging: // Add drag done
487 if (Keyboard::is_insert_note_event(ev) || trackview.editor().current_mouse_mode() == MouseRange){
489 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
491 const double x = _drag_rect->property_x1();
492 const double length = trackview.editor().pixel_to_frame (_drag_rect->property_x2() - _drag_rect->property_x1());
494 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), true);
501 create_ghost_note (ev->x, ev->y);
511 MidiRegionView::motion (GdkEventMotion* ev)
513 double event_x, event_y;
514 framepos_t event_frame = 0;
518 group->w2i(event_x, event_y);
520 // convert event_x to global frame
521 event_frame = snap_pixel_to_frame (event_x);
523 if (!_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
524 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())
525 && _mouse_state != AddDragging){
527 create_ghost_note (ev->x, ev->y);
529 else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
530 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())){
532 update_ghost_note (ev->x, ev->y);
534 else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange){
539 trackview.editor().verbose_cursor()->hide ();
541 else if (_ghost_note && trackview.editor().current_mouse_mode() == MouseRange) {
542 update_ghost_note (ev->x, ev->y);
545 /* any motion immediately hides velocity text that may have been visible */
547 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
548 (*i)->hide_velocity ();
551 switch (_mouse_state) {
552 case Pressed: // Maybe start a drag, if we've moved a bit
554 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
555 /* no appreciable movement since the button was pressed */
560 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject
561 && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
563 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
564 Gdk::Cursor(Gdk::FLEUR), ev->time);
568 _drag_start_x = event_x;
569 _drag_start_y = event_y;
571 _drag_rect = new ArdourCanvas::SimpleRect(*group);
572 _drag_rect->property_x1() = event_x;
573 _drag_rect->property_y1() = event_y;
574 _drag_rect->property_x2() = event_x;
575 _drag_rect->property_y2() = event_y;
576 _drag_rect->property_outline_what() = 0xFF;
577 _drag_rect->property_outline_color_rgba()
578 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
579 _drag_rect->property_fill_color_rgba()
580 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
582 _mouse_state = SelectRectDragging;
585 // Add note drag start
586 } else if (trackview.editor().internal_editing()) {
591 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
592 Gdk::Cursor(Gdk::FLEUR), ev->time);
596 _drag_start_x = event_x;
597 _drag_start_y = event_y;
599 _drag_rect = new ArdourCanvas::SimpleRect(*group);
600 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
602 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
603 midi_stream_view()->y_to_note(event_y));
604 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
605 _drag_rect->property_y2() = _drag_rect->property_y1()
606 + floor(midi_stream_view()->note_height());
607 _drag_rect->property_outline_what() = 0xFF;
608 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
609 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
611 _mouse_state = AddDragging;
618 trackview.editor().verbose_cursor()->hide ();
626 case SelectRectDragging: // Select drag motion
627 case AddDragging: // Add note drag motion
632 GdkModifierType state;
633 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
638 if (_mouse_state == AddDragging){
639 event_x = trackview.editor().frame_to_pixel(event_frame);
644 if (event_x > _drag_start_x){
645 _drag_rect->property_x2() = event_x;
648 _drag_rect->property_x1() = event_x;
652 if (_drag_rect && _mouse_state == SelectRectDragging) {
654 if (event_y > _drag_start_y){
655 _drag_rect->property_y2() = event_y;
658 _drag_rect->property_y1() = event_y;
661 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
667 case SelectTouchDragging:
679 MidiRegionView::scroll (GdkEventScroll* ev)
681 if (_selection.empty()) {
685 trackview.editor().verbose_cursor()->hide ();
687 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
689 if (ev->direction == GDK_SCROLL_UP) {
690 change_velocities (true, fine, false);
691 } else if (ev->direction == GDK_SCROLL_DOWN) {
692 change_velocities (false, fine, false);
698 MidiRegionView::key_press (GdkEventKey* ev)
700 /* since GTK bindings are generally activated on press, and since
701 detectable auto-repeat is the name of the game and only sends
702 repeated presses, carry out key actions at key press, not release.
705 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
706 _mouse_state = SelectTouchDragging;
709 } else if (ev->keyval == GDK_Escape) {
713 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
715 bool start = (ev->keyval == GDK_comma);
716 bool end = (ev->keyval == GDK_period);
717 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
718 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
720 change_note_lengths (fine, shorter, 0.0, start, end);
724 } else if (ev->keyval == GDK_Delete) {
729 } else if (ev->keyval == GDK_Tab) {
731 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
732 goto_previous_note ();
738 } else if (ev->keyval == GDK_Up) {
740 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
741 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
743 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
744 change_velocities (true, fine, allow_smush);
746 transpose (true, fine, allow_smush);
750 } else if (ev->keyval == GDK_Down) {
752 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
753 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
755 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
756 change_velocities (false, fine, allow_smush);
758 transpose (false, fine, allow_smush);
762 } else if (ev->keyval == GDK_Left) {
767 } else if (ev->keyval == GDK_Right) {
772 } else if (ev->keyval == GDK_Control_L) {
781 MidiRegionView::key_release (GdkEventKey* ev)
783 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
791 MidiRegionView::show_list_editor ()
794 _list_editor = new MidiListEditor (trackview.session(), midi_region());
796 _list_editor->present ();
799 /** Add a note to the model, and the view, at a canvas (click) coordinate.
800 * \param x horizontal position in pixels
801 * \param y vertical position in pixels
802 * \param length duration of the note in beats, which will be snapped to the grid
803 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
806 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
808 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
809 MidiStreamView* const view = mtv->midi_view();
811 double note = view->y_to_note(y);
814 assert(note <= 127.0);
816 // Start of note in frames relative to region start
817 framepos_t const start_frames = snap_pixel_to_frame (x);
818 assert(start_frames >= 0);
821 length = frames_to_beats(
822 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
824 assert (length != 0);
827 length = frames_to_beats (beats_to_frames (length) - 1);
830 const boost::shared_ptr<NoteType> new_note (new NoteType (get_channel_for_add (),
831 frames_to_beats(start_frames + _region->start()), length,
832 (uint8_t)note, 0x40));
834 if (_model->contains (new_note)) {
838 view->update_note_range(new_note->note());
840 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note");
842 _model->apply_command(*trackview.session(), cmd);
844 play_midi_note (new_note);
848 MidiRegionView::clear_events()
853 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
854 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
859 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
864 _patch_changes.clear();
866 _optimization_iterator = _events.end();
870 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
874 content_connection.disconnect ();
875 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
879 if (_enable_display) {
885 MidiRegionView::start_note_diff_command (string name)
887 if (!_note_diff_command) {
888 _note_diff_command = _model->new_note_diff_command (name);
893 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
895 if (_note_diff_command) {
896 _note_diff_command->add (note);
899 _marked_for_selection.insert(note);
902 _marked_for_velocity.insert(note);
907 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
909 if (_note_diff_command && ev->note()) {
910 _note_diff_command->remove(ev->note());
915 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
916 MidiModel::NoteDiffCommand::Property property,
919 if (_note_diff_command) {
920 _note_diff_command->change (ev->note(), property, val);
925 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
926 MidiModel::NoteDiffCommand::Property property,
927 Evoral::MusicalTime val)
929 if (_note_diff_command) {
930 _note_diff_command->change (ev->note(), property, val);
935 MidiRegionView::apply_diff (bool as_subcommand)
939 if (!_note_diff_command) {
943 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
944 // Mark all selected notes for selection when model reloads
945 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
946 _marked_for_selection.insert((*i)->note());
951 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
953 _model->apply_command (*trackview.session(), _note_diff_command);
956 _note_diff_command = 0;
957 midi_view()->midi_track()->playlist_modified();
960 _marked_for_selection.clear();
963 _marked_for_velocity.clear();
967 MidiRegionView::abort_command()
969 delete _note_diff_command;
970 _note_diff_command = 0;
975 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
977 if (_optimization_iterator != _events.end()) {
978 ++_optimization_iterator;
981 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
982 return *_optimization_iterator;
985 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
986 if ((*_optimization_iterator)->note() == note) {
987 return *_optimization_iterator;
995 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
997 MidiModel::Notes notes;
998 _model->get_notes (notes, op, val, chan_mask);
1000 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1001 CanvasNoteEvent* cne = find_canvas_note (*n);
1009 MidiRegionView::redisplay_model()
1011 // Don't redisplay the model if we're currently recording and displaying that
1012 if (_active_notes) {
1020 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1021 (*i)->invalidate ();
1024 MidiModel::ReadLock lock(_model->read_lock());
1026 MidiModel::Notes& notes (_model->notes());
1027 _optimization_iterator = _events.begin();
1029 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1031 boost::shared_ptr<NoteType> note (*n);
1032 CanvasNoteEvent* cne;
1035 if (note_in_region_range (note, visible)) {
1037 if ((cne = find_canvas_note (note)) != 0) {
1044 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1046 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1058 add_note (note, visible);
1063 if ((cne = find_canvas_note (note)) != 0) {
1071 /* remove note items that are no longer valid */
1073 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1074 if (!(*i)->valid ()) {
1076 i = _events.erase (i);
1082 _patch_changes.clear();
1086 display_patch_changes ();
1088 _marked_for_selection.clear ();
1089 _marked_for_velocity.clear ();
1091 /* we may have caused _events to contain things out of order (e.g. if a note
1092 moved earlier or later). we don't generally need them in time order, but
1093 make a note that a sort is required for those cases that require it.
1096 _sort_needed = true;
1100 MidiRegionView::display_patch_changes ()
1102 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1103 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1105 for (uint8_t i = 0; i < 16; ++i) {
1106 if (chn_mask & (1<<i)) {
1107 display_patch_changes_on_channel (i);
1113 MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
1115 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1117 if ((*i)->channel() != channel) {
1121 MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
1123 boost::shared_ptr<MIDI::Name::Patch> patch =
1124 MIDI::Name::MidiPatchManager::instance().find_patch(
1125 _model_name, _custom_device_mode, channel, patch_key);
1128 add_canvas_patch_change (*i, patch->name());
1131 /* program and bank numbers are zero-based: convert to one-based */
1132 snprintf (buf, 16, "%d\n%d", (*i)->program() + 1, (*i)->bank() + 1);
1133 add_canvas_patch_change (*i, buf);
1139 MidiRegionView::display_sysexes()
1141 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1142 Evoral::MusicalTime time = (*i)->time();
1147 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1148 str << int((*i)->buffer()[b]);
1149 if (b != (*i)->size() -1) {
1153 string text = str.str();
1155 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1157 double height = midi_stream_view()->contents_height();
1159 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1160 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1162 // Show unless patch change is beyond the region bounds
1163 if (time - _region->start() >= _region->length() || time < _region->start()) {
1169 _sys_exes.push_back(sysex);
1174 MidiRegionView::~MidiRegionView ()
1176 in_destructor = true;
1178 trackview.editor().verbose_cursor()->hide ();
1180 note_delete_connection.disconnect ();
1182 delete _list_editor;
1184 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1186 if (_active_notes) {
1194 delete _note_diff_command;
1195 delete _step_edit_cursor;
1196 delete _temporary_note_group;
1200 MidiRegionView::region_resized (const PropertyChange& what_changed)
1202 RegionView::region_resized(what_changed);
1204 if (what_changed.contains (ARDOUR::Properties::position)) {
1205 set_duration(_region->length(), 0);
1206 if (_enable_display) {
1213 MidiRegionView::reset_width_dependent_items (double pixel_width)
1215 RegionView::reset_width_dependent_items(pixel_width);
1216 assert(_pixel_width == pixel_width);
1218 if (_enable_display) {
1222 move_step_edit_cursor (_step_edit_cursor_position);
1223 set_step_edit_cursor_width (_step_edit_cursor_width);
1227 MidiRegionView::set_height (double height)
1229 static const double FUDGE = 2.0;
1230 const double old_height = _height;
1231 RegionView::set_height(height);
1232 _height = height - FUDGE;
1234 apply_note_range(midi_stream_view()->lowest_note(),
1235 midi_stream_view()->highest_note(),
1236 height != old_height + FUDGE);
1239 name_pixbuf->raise_to_top();
1242 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1243 (*x)->set_height (midi_stream_view()->contents_height());
1246 if (_step_edit_cursor) {
1247 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1252 /** Apply the current note range from the stream view
1253 * by repositioning/hiding notes as necessary
1256 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1258 if (!_enable_display) {
1262 if (!force && _current_range_min == min && _current_range_max == max) {
1266 _current_range_min = min;
1267 _current_range_max = max;
1269 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1270 CanvasNoteEvent* event = *i;
1271 boost::shared_ptr<NoteType> note (event->note());
1273 if (note->note() < _current_range_min ||
1274 note->note() > _current_range_max) {
1280 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1282 const double y1 = midi_stream_view()->note_to_y(note->note());
1283 const double y2 = y1 + floor(midi_stream_view()->note_height());
1285 cnote->property_y1() = y1;
1286 cnote->property_y2() = y2;
1288 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1290 const double diamond_size = update_hit (chit);
1292 chit->set_height (diamond_size);
1298 MidiRegionView::add_ghost (TimeAxisView& tv)
1302 double unit_position = _region->position () / samples_per_unit;
1303 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1304 MidiGhostRegion* ghost;
1306 if (mtv && mtv->midi_view()) {
1307 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1308 to allow having midi notes on top of note lines and waveforms.
1310 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1312 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1315 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1316 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1317 ghost->add_note(note);
1321 ghost->set_height ();
1322 ghost->set_duration (_region->length() / samples_per_unit);
1323 ghosts.push_back (ghost);
1325 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1331 /** Begin tracking note state for successive calls to add_event
1334 MidiRegionView::begin_write()
1336 assert(!_active_notes);
1337 _active_notes = new CanvasNote*[128];
1338 for (unsigned i=0; i < 128; ++i) {
1339 _active_notes[i] = 0;
1344 /** Destroy note state for add_event
1347 MidiRegionView::end_write()
1349 delete[] _active_notes;
1351 _marked_for_selection.clear();
1352 _marked_for_velocity.clear();
1356 /** Resolve an active MIDI note (while recording).
1359 MidiRegionView::resolve_note(uint8_t note, double end_time)
1361 if (midi_view()->note_mode() != Sustained) {
1365 if (_active_notes && _active_notes[note]) {
1367 const framepos_t end_time_frames = beats_to_frames(end_time);
1369 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1370 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1371 _active_notes[note] = 0;
1376 /** Extend active notes to rightmost edge of region (if length is changed)
1379 MidiRegionView::extend_active_notes()
1381 if (!_active_notes) {
1385 for (unsigned i=0; i < 128; ++i) {
1386 if (_active_notes[i]) {
1387 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1394 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1396 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1400 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1402 if (!route_ui || !route_ui->midi_track()) {
1406 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1412 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1414 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1418 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1420 if (!route_ui || !route_ui->midi_track()) {
1424 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1426 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1435 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1437 const framepos_t note_start_frames = beats_to_frames(note->time());
1439 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1440 (note_start_frames < _region->start());
1442 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1443 (note->note() <= midi_stream_view()->highest_note());
1448 /** Update a canvas note's size from its model note.
1449 * @param ev Canvas note to update.
1450 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1453 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1455 boost::shared_ptr<NoteType> note = ev->note();
1457 const framepos_t note_start_frames = beats_to_frames(note->time());
1459 /* trim note display to not overlap the end of its region */
1460 const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1462 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1463 const double y1 = midi_stream_view()->note_to_y(note->note());
1464 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1466 ev->property_x1() = x;
1467 ev->property_y1() = y1;
1469 if (note->length() > 0) {
1470 ev->property_x2() = note_endpixel;
1472 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1475 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1477 if (note->length() == 0) {
1478 if (_active_notes) {
1479 assert(note->note() < 128);
1480 // If this note is already active there's a stuck note,
1481 // finish the old note rectangle
1482 if (_active_notes[note->note()]) {
1483 CanvasNote* const old_rect = _active_notes[note->note()];
1484 boost::shared_ptr<NoteType> old_note = old_rect->note();
1485 old_rect->property_x2() = x;
1486 old_rect->property_outline_what() = (guint32) 0xF;
1488 _active_notes[note->note()] = ev;
1490 /* outline all but right edge */
1491 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1493 /* outline all edges */
1494 ev->property_outline_what() = (guint32) 0xF;
1497 if (update_ghost_regions) {
1498 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1499 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1501 gr->update_note (ev);
1508 MidiRegionView::update_hit (CanvasHit* ev)
1510 boost::shared_ptr<NoteType> note = ev->note();
1512 const framepos_t note_start_frames = beats_to_frames(note->time());
1513 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1514 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1515 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1519 return diamond_size;
1522 /** Add a MIDI note to the view (with length).
1524 * If in sustained mode, notes with length 0 will be considered active
1525 * notes, and resolve_note should be called when the corresponding note off
1526 * event arrives, to properly display the note.
1529 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1531 CanvasNoteEvent* event = 0;
1533 assert(note->time() >= 0);
1534 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1536 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1538 if (midi_view()->note_mode() == Sustained) {
1540 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1542 update_note (ev_rect);
1546 MidiGhostRegion* gr;
1548 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1549 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1550 gr->add_note(ev_rect);
1554 } else if (midi_view()->note_mode() == Percussive) {
1556 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1558 CanvasHit* ev_diamond = new CanvasHit(*this, *_note_group, diamond_size, note);
1560 update_hit (ev_diamond);
1569 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1570 note_selected(event, true);
1573 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1574 event->show_velocity();
1577 event->on_channel_selection_change(_last_channel_selection);
1578 _events.push_back(event);
1587 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1588 MidiStreamView* const view = mtv->midi_view();
1590 view->update_note_range(note->note());
1594 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1595 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1597 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1599 /* potentially extend region to hold new note */
1601 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1602 framepos_t region_end = _region->position() + _region->length() - 1;
1604 if (end_frame > region_end) {
1605 _region->set_length (end_frame - _region->position(), this);
1608 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1609 MidiStreamView* const view = mtv->midi_view();
1611 view->update_note_range(new_note->note());
1613 _marked_for_selection.clear ();
1616 start_note_diff_command (_("step add"));
1617 note_diff_add_note (new_note, true, false);
1620 // last_step_edit_note = new_note;
1624 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1626 change_note_lengths (false, false, beats, false, true);
1630 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
1632 assert (patch->time() >= 0);
1634 const double x = trackview.editor().frame_to_pixel (beats_to_frames (patch->time()));
1636 double const height = midi_stream_view()->contents_height();
1638 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1639 new CanvasPatchChange(*this, *_note_group,
1644 _custom_device_mode,
1648 // Show unless patch change is beyond the region bounds
1649 if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1650 patch_change->hide();
1652 patch_change->show();
1655 _patch_changes.push_back (patch_change);
1659 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1661 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1662 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1666 if (i != _model->patch_changes().end()) {
1667 key.msb = (*i)->bank_msb ();
1668 key.lsb = (*i)->bank_lsb ();
1669 key.program_number = (*i)->program ();
1671 key.msb = key.lsb = key.program_number = 0;
1674 assert (key.is_sane());
1679 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1681 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1683 if (pc.patch()->program() != new_patch.program_number) {
1684 c->change_program (pc.patch (), new_patch.program_number);
1687 int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1688 if (pc.patch()->bank() != new_bank) {
1689 c->change_bank (pc.patch (), new_bank);
1692 _model->apply_command (*trackview.session(), c);
1694 _patch_changes.clear ();
1695 display_patch_changes ();
1699 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1701 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1703 if (old_change->time() != new_change.time()) {
1704 c->change_time (old_change, new_change.time());
1707 if (old_change->channel() != new_change.channel()) {
1708 c->change_channel (old_change, new_change.channel());
1711 if (old_change->program() != new_change.program()) {
1712 c->change_program (old_change, new_change.program());
1715 if (old_change->bank() != new_change.bank()) {
1716 c->change_bank (old_change, new_change.bank());
1719 _model->apply_command (*trackview.session(), c);
1721 _patch_changes.clear ();
1722 display_patch_changes ();
1725 /** Add a patch change to the region.
1726 * @param t Time in frames relative to region position
1727 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1728 * get_channel_for_add())
1731 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1733 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1734 c->add (MidiModel::PatchChangePtr (
1735 new Evoral::PatchChange<Evoral::MusicalTime> (
1736 frames_to_beats (t + midi_region()->start()), get_channel_for_add(), patch.program(), patch.bank()
1740 _model->apply_command (*trackview.session(), c);
1742 _patch_changes.clear ();
1743 display_patch_changes ();
1747 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1749 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1750 c->change_time (pc.patch (), t);
1751 _model->apply_command (*trackview.session(), c);
1753 _patch_changes.clear ();
1754 display_patch_changes ();
1758 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1760 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1761 c->remove (pc->patch ());
1762 _model->apply_command (*trackview.session(), c);
1764 _patch_changes.clear ();
1765 display_patch_changes ();
1769 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1771 if (patch.patch()->program() < 127) {
1772 MIDI::Name::PatchPrimaryKey key;
1773 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1774 key.program_number++;
1775 change_patch_change (patch, key);
1780 MidiRegionView::next_patch (CanvasPatchChange& patch)
1782 if (patch.patch()->program() > 0) {
1783 MIDI::Name::PatchPrimaryKey key;
1784 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1785 key.program_number--;
1786 change_patch_change (patch, key);
1791 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1793 if (_selection.empty()) {
1797 if (_selection.erase (cne) > 0) {
1798 cerr << "Erased a CNE from selection\n";
1803 MidiRegionView::delete_selection()
1805 if (_selection.empty()) {
1809 start_note_diff_command (_("delete selection"));
1811 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1812 if ((*i)->selected()) {
1813 _note_diff_command->remove((*i)->note());
1823 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1825 start_note_diff_command (_("delete note"));
1826 _note_diff_command->remove (n);
1829 trackview.editor().verbose_cursor()->hide ();
1833 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1835 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1836 if ((*i)->selected() && (*i) != ev) {
1837 (*i)->set_selected(false);
1838 (*i)->hide_velocity();
1846 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1848 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1851 Selection::iterator tmp = i;
1854 (*i)->set_selected (false);
1855 _selection.erase (i);
1864 /* don't bother with removing this regionview from the editor selection,
1865 since we're about to add another note, and thus put/keep this
1866 regionview in the editor selection.
1869 if (!ev->selected()) {
1870 add_to_selection (ev);
1875 MidiRegionView::select_all_notes ()
1879 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1880 add_to_selection (*i);
1885 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1887 uint8_t low_note = 127;
1888 uint8_t high_note = 0;
1889 MidiModel::Notes& notes (_model->notes());
1890 _optimization_iterator = _events.begin();
1896 if (extend && _selection.empty()) {
1902 /* scan existing selection to get note range */
1904 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1905 if ((*i)->note()->note() < low_note) {
1906 low_note = (*i)->note()->note();
1908 if ((*i)->note()->note() > high_note) {
1909 high_note = (*i)->note()->note();
1913 low_note = min (low_note, notenum);
1914 high_note = max (high_note, notenum);
1917 _no_sound_notes = true;
1919 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1921 boost::shared_ptr<NoteType> note (*n);
1922 CanvasNoteEvent* cne;
1923 bool select = false;
1925 if (((1 << note->channel()) & channel_mask) != 0) {
1927 if ((note->note() >= low_note && note->note() <= high_note)) {
1930 } else if (note->note() == notenum) {
1936 if ((cne = find_canvas_note (note)) != 0) {
1937 // extend is false because we've taken care of it,
1938 // since it extends by time range, not pitch.
1939 note_selected (cne, add, false);
1943 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1947 _no_sound_notes = false;
1951 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1953 MidiModel::Notes& notes (_model->notes());
1954 _optimization_iterator = _events.begin();
1956 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1958 boost::shared_ptr<NoteType> note (*n);
1959 CanvasNoteEvent* cne;
1961 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1962 if ((cne = find_canvas_note (note)) != 0) {
1963 if (cne->selected()) {
1964 note_deselected (cne);
1966 note_selected (cne, true, false);
1974 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1977 clear_selection_except(ev);
1982 if (!ev->selected()) {
1983 add_to_selection (ev);
1987 /* find end of latest note selected, select all between that and the start of "ev" */
1989 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1990 Evoral::MusicalTime latest = 0;
1992 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1993 if ((*i)->note()->end_time() > latest) {
1994 latest = (*i)->note()->end_time();
1996 if ((*i)->note()->time() < earliest) {
1997 earliest = (*i)->note()->time();
2001 if (ev->note()->end_time() > latest) {
2002 latest = ev->note()->end_time();
2005 if (ev->note()->time() < earliest) {
2006 earliest = ev->note()->time();
2009 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2011 /* find notes entirely within OR spanning the earliest..latest range */
2013 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2014 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2015 add_to_selection (*i);
2019 /* if events were guaranteed to be time sorted, we could do this.
2020 but as of sept 10th 2009, they no longer are.
2023 if ((*i)->note()->time() > latest) {
2032 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2034 remove_from_selection (ev);
2038 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
2048 // TODO: Make this faster by storing the last updated selection rect, and only
2049 // adjusting things that are in the area that appears/disappeared.
2050 // We probably need a tree to be able to find events in O(log(n)) time.
2052 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2054 /* check if any corner of the note is inside the rect
2057 1) this is computing "touched by", not "contained by" the rect.
2058 2) this does not require that events be sorted in time.
2061 const double ix1 = (*i)->x1();
2062 const double ix2 = (*i)->x2();
2063 const double iy1 = (*i)->y1();
2064 const double iy2 = (*i)->y2();
2066 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2067 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2068 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2069 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2072 if (!(*i)->selected()) {
2073 add_to_selection (*i);
2075 } else if ((*i)->selected()) {
2076 // Not inside rectangle
2077 remove_from_selection (*i);
2083 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2085 Selection::iterator i = _selection.find (ev);
2087 if (i != _selection.end()) {
2088 _selection.erase (i);
2091 ev->set_selected (false);
2092 ev->hide_velocity ();
2094 if (_selection.empty()) {
2095 PublicEditor& editor (trackview.editor());
2096 editor.get_selection().remove (this);
2101 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2103 bool add_mrv_selection = false;
2105 if (_selection.empty()) {
2106 add_mrv_selection = true;
2109 if (_selection.insert (ev).second) {
2110 ev->set_selected (true);
2111 play_midi_note ((ev)->note());
2114 if (add_mrv_selection) {
2115 PublicEditor& editor (trackview.editor());
2116 editor.get_selection().add (this);
2121 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2123 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2124 PossibleChord to_play;
2125 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2127 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2128 if ((*i)->note()->time() < earliest) {
2129 earliest = (*i)->note()->time();
2133 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2134 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2135 to_play.push_back ((*i)->note());
2137 (*i)->move_event(dx, dy);
2140 if (dy && !_selection.empty() && !_no_sound_notes && trackview.editor().sound_notes()) {
2142 if (to_play.size() > 1) {
2144 PossibleChord shifted;
2146 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2147 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2148 moved_note->set_note (moved_note->note() + cumulative_dy);
2149 shifted.push_back (moved_note);
2152 play_midi_chord (shifted);
2154 } else if (!to_play.empty()) {
2156 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2157 moved_note->set_note (moved_note->note() + cumulative_dy);
2158 play_midi_note (moved_note);
2164 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2166 assert (!_selection.empty());
2168 uint8_t lowest_note_in_selection = 127;
2169 uint8_t highest_note_in_selection = 0;
2170 uint8_t highest_note_difference = 0;
2172 // find highest and lowest notes first
2174 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2175 uint8_t pitch = (*i)->note()->note();
2176 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2177 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2181 cerr << "dnote: " << (int) dnote << endl;
2182 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2183 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2184 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2185 << int(highest_note_in_selection) << endl;
2186 cerr << "selection size: " << _selection.size() << endl;
2187 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2190 // Make sure the note pitch does not exceed the MIDI standard range
2191 if (highest_note_in_selection + dnote > 127) {
2192 highest_note_difference = highest_note_in_selection - 127;
2195 start_note_diff_command (_("move notes"));
2197 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2199 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2205 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2207 uint8_t original_pitch = (*i)->note()->note();
2208 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2210 // keep notes in standard midi range
2211 clamp_to_0_127(new_pitch);
2213 // keep original pitch if note is dragged outside valid midi range
2214 if ((original_pitch != 0 && new_pitch == 0)
2215 || (original_pitch != 127 && new_pitch == 127)) {
2216 new_pitch = original_pitch;
2219 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2220 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2222 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2227 // care about notes being moved beyond the upper/lower bounds on the canvas
2228 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2229 highest_note_in_selection > midi_stream_view()->highest_note()) {
2230 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2235 MidiRegionView::snap_pixel_to_frame(double x)
2237 PublicEditor& editor (trackview.editor());
2238 return snap_frame_to_frame (editor.pixel_to_frame (x));
2241 /** Snap a frame offset within our region using the current snap settings.
2242 * @param x Frame offset from this region's position.
2243 * @return Snapped frame offset from this region's position.
2246 MidiRegionView::snap_frame_to_frame (frameoffset_t x)
2248 PublicEditor& editor = trackview.editor();
2250 /* x is region relative, convert it to global absolute frames */
2251 framepos_t const session_frame = x + _region->position();
2253 /* try a snap in either direction */
2254 framepos_t frame = session_frame;
2255 editor.snap_to (frame, 0);
2257 /* if we went off the beginning of the region, snap forwards */
2258 if (frame < _region->position ()) {
2259 frame = session_frame;
2260 editor.snap_to (frame, 1);
2263 /* back to region relative */
2264 return frame - _region->position();
2268 MidiRegionView::snap_to_pixel(double x)
2270 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2274 MidiRegionView::get_position_pixels()
2276 framepos_t region_frame = get_position();
2277 return trackview.editor().frame_to_pixel(region_frame);
2281 MidiRegionView::get_end_position_pixels()
2283 framepos_t frame = get_position() + get_duration ();
2284 return trackview.editor().frame_to_pixel(frame);
2288 MidiRegionView::beats_to_frames(double beats) const
2290 return _time_converter.to(beats);
2294 MidiRegionView::frames_to_beats(framepos_t frames) const
2296 return _time_converter.from(frames);
2300 MidiRegionView::begin_resizing (bool /*at_front*/)
2302 _resize_data.clear();
2304 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2305 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2307 // only insert CanvasNotes into the map
2309 NoteResizeData *resize_data = new NoteResizeData();
2310 resize_data->canvas_note = note;
2312 // create a new SimpleRect from the note which will be the resize preview
2313 SimpleRect *resize_rect = new SimpleRect(
2314 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2316 // calculate the colors: get the color settings
2317 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2318 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2321 // make the resize preview notes more transparent and bright
2322 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2324 // calculate color based on note velocity
2325 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2326 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2330 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2331 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2333 resize_data->resize_rect = resize_rect;
2334 _resize_data.push_back(resize_data);
2339 /** Update resizing notes while user drags.
2340 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2341 * @param at_front which end of the note (true == note on, false == note off)
2342 * @param delta_x change in mouse position since the start of the drag
2343 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2344 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2345 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2346 * as the \a primary note.
2349 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2351 bool cursor_set = false;
2353 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2354 SimpleRect* resize_rect = (*i)->resize_rect;
2355 CanvasNote* canvas_note = (*i)->canvas_note;
2360 current_x = canvas_note->x1() + delta_x;
2362 current_x = primary->x1() + delta_x;
2366 current_x = canvas_note->x2() + delta_x;
2368 current_x = primary->x2() + delta_x;
2373 resize_rect->property_x1() = snap_to_pixel(current_x);
2374 resize_rect->property_x2() = canvas_note->x2();
2376 resize_rect->property_x2() = snap_to_pixel(current_x);
2377 resize_rect->property_x1() = canvas_note->x1();
2383 beats = snap_pixel_to_frame (current_x);
2384 beats = frames_to_beats (beats);
2389 if (beats < canvas_note->note()->end_time()) {
2390 len = canvas_note->note()->time() - beats;
2391 len += canvas_note->note()->length();
2396 if (beats >= canvas_note->note()->time()) {
2397 len = beats - canvas_note->note()->time();
2404 snprintf (buf, sizeof (buf), "%.3g beats", len);
2405 show_verbose_cursor (buf, 0, 0);
2414 /** Finish resizing notes when the user releases the mouse button.
2415 * Parameters the same as for \a update_resizing().
2418 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2420 start_note_diff_command (_("resize notes"));
2422 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2423 CanvasNote* canvas_note = (*i)->canvas_note;
2424 SimpleRect* resize_rect = (*i)->resize_rect;
2429 current_x = canvas_note->x1() + delta_x;
2431 current_x = primary->x1() + delta_x;
2435 current_x = canvas_note->x2() + delta_x;
2437 current_x = primary->x2() + delta_x;
2441 current_x = snap_pixel_to_frame (current_x);
2442 current_x = frames_to_beats (current_x);
2444 if (at_front && current_x < canvas_note->note()->end_time()) {
2445 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2447 double len = canvas_note->note()->time() - current_x;
2448 len += canvas_note->note()->length();
2451 /* XXX convert to beats */
2452 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2457 double len = current_x - canvas_note->note()->time();
2460 /* XXX convert to beats */
2461 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2469 _resize_data.clear();
2474 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2476 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, (uint8_t) channel);
2480 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2482 uint8_t new_velocity;
2485 new_velocity = event->note()->velocity() + velocity;
2486 clamp_to_0_127(new_velocity);
2488 new_velocity = velocity;
2491 event->set_selected (event->selected()); // change color
2493 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2497 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2502 new_note = event->note()->note() + note;
2507 clamp_to_0_127 (new_note);
2508 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2512 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2514 bool change_start = false;
2515 bool change_length = false;
2516 Evoral::MusicalTime new_start = 0;
2517 Evoral::MusicalTime new_length = 0;
2519 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2521 front_delta: if positive - move the start of the note later in time (shortening it)
2522 if negative - move the start of the note earlier in time (lengthening it)
2524 end_delta: if positive - move the end of the note later in time (lengthening it)
2525 if negative - move the end of the note earlier in time (shortening it)
2529 if (front_delta < 0) {
2531 if (event->note()->time() < -front_delta) {
2534 new_start = event->note()->time() + front_delta; // moves earlier
2537 /* start moved toward zero, so move the end point out to where it used to be.
2538 Note that front_delta is negative, so this increases the length.
2541 new_length = event->note()->length() - front_delta;
2542 change_start = true;
2543 change_length = true;
2547 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2549 if (new_pos < event->note()->end_time()) {
2550 new_start = event->note()->time() + front_delta;
2551 /* start moved toward the end, so move the end point back to where it used to be */
2552 new_length = event->note()->length() - front_delta;
2553 change_start = true;
2554 change_length = true;
2561 bool can_change = true;
2562 if (end_delta < 0) {
2563 if (event->note()->length() < -end_delta) {
2569 new_length = event->note()->length() + end_delta;
2570 change_length = true;
2575 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2578 if (change_length) {
2579 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2584 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2586 Evoral::MusicalTime new_time;
2590 if (event->note()->time() < -delta) {
2593 new_time = event->note()->time() + delta;
2596 new_time = event->note()->time() + delta;
2602 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2606 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2608 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2612 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2616 if (_selection.empty()) {
2631 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2632 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2638 start_note_diff_command (_("change velocities"));
2640 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2641 Selection::iterator next = i;
2643 change_note_velocity (*i, delta, true);
2649 if (!_selection.empty()) {
2651 snprintf (buf, sizeof (buf), "Vel %d",
2652 (int) (*_selection.begin())->note()->velocity());
2653 show_verbose_cursor (buf, 10, 10);
2659 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2661 if (_selection.empty()) {
2678 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2680 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2684 if ((int8_t) (*i)->note()->note() + delta > 127) {
2691 start_note_diff_command (_("transpose"));
2693 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2694 Selection::iterator next = i;
2696 change_note_note (*i, delta, true);
2704 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2710 /* grab the current grid distance */
2712 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2714 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2715 cerr << "Grid type not available as beats - TO BE FIXED\n";
2725 start_note_diff_command (_("change note lengths"));
2727 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2728 Selection::iterator next = i;
2731 /* note the negation of the delta for start */
2733 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2742 MidiRegionView::nudge_notes (bool forward)
2744 if (_selection.empty()) {
2748 /* pick a note as the point along the timeline to get the nudge distance.
2749 its not necessarily the earliest note, so we may want to pull the notes out
2750 into a vector and sort before using the first one.
2753 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2755 framepos_t distance;
2757 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2759 /* grid is off - use nudge distance */
2761 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2767 framepos_t next_pos = ref_point;
2770 if (max_framepos - 1 < next_pos) {
2774 if (next_pos == 0) {
2780 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2781 distance = ref_point - next_pos;
2784 if (distance == 0) {
2788 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2794 start_note_diff_command (_("nudge"));
2796 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2797 Selection::iterator next = i;
2799 change_note_time (*i, delta, true);
2807 MidiRegionView::change_channel(uint8_t channel)
2809 start_note_diff_command(_("change channel"));
2810 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2811 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2819 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2821 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2823 _pre_enter_cursor = editor->get_canvas_cursor ();
2825 if (_mouse_state == SelectTouchDragging) {
2826 note_selected (ev, true);
2829 show_verbose_cursor (ev->note ());
2833 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2835 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2837 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2838 (*i)->hide_velocity ();
2841 editor->verbose_cursor()->hide ();
2843 if (_pre_enter_cursor) {
2844 editor->set_canvas_cursor (_pre_enter_cursor);
2845 _pre_enter_cursor = 0;
2850 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
2853 s << ((int) ev->patch()->program() + 1) << ":" << (ev->patch()->bank() + 1);
2854 show_verbose_cursor (s.str(), 10, 20);
2858 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
2860 trackview.editor().verbose_cursor()->hide ();
2864 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
2866 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2868 if (x_fraction > 0.0 && x_fraction < 0.25) {
2869 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
2870 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
2871 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
2873 if (_pre_enter_cursor && can_set_cursor) {
2874 editor->set_canvas_cursor (_pre_enter_cursor);
2880 MidiRegionView::set_frame_color()
2884 TimeAxisViewItem::set_frame_color ();
2891 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2892 } else if (high_enough_for_name) {
2893 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2898 if (!rect_visible) {
2899 f = UINT_RGBA_CHANGE_A (f, 0);
2902 frame->property_fill_color_rgba() = f;
2906 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2910 case FilterChannels:
2911 _force_channel = -1;
2914 _force_channel = mask;
2915 mask = 0xFFFF; // Show all notes as active (below)
2918 // Update notes for selection
2919 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2920 (*i)->on_channel_selection_change(mask);
2923 _last_channel_selection = mask;
2925 _patch_changes.clear ();
2926 display_patch_changes ();
2930 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2932 _model_name = model;
2933 _custom_device_mode = custom_device_mode;
2938 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2940 if (_selection.empty()) {
2944 PublicEditor& editor (trackview.editor());
2949 editor.get_cut_buffer().add (selection_as_cut_buffer());
2957 start_note_diff_command();
2959 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2965 note_diff_remove_note (*i);
2975 MidiRegionView::selection_as_cut_buffer () const
2979 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2980 NoteType* n = (*i)->note().get();
2981 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2984 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2990 /** This method handles undo */
2992 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
2998 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3000 trackview.session()->begin_reversible_command (_("paste"));
3002 start_note_diff_command (_("paste"));
3004 Evoral::MusicalTime beat_delta;
3005 Evoral::MusicalTime paste_pos_beats;
3006 Evoral::MusicalTime duration;
3007 Evoral::MusicalTime end_point = 0;
3009 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3010 paste_pos_beats = frames_to_beats (pos - _region->position());
3011 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3012 paste_pos_beats = 0;
3014 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",
3015 (*mcb.notes().begin())->time(),
3016 (*mcb.notes().rbegin())->end_time(),
3017 duration, pos, _region->position(),
3018 paste_pos_beats, beat_delta));
3022 for (int n = 0; n < (int) times; ++n) {
3024 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3026 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3027 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3029 /* make all newly added notes selected */
3031 note_diff_add_note (copied_note, true);
3032 end_point = copied_note->end_time();
3035 paste_pos_beats += duration;
3038 /* if we pasted past the current end of the region, extend the region */
3040 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
3041 framepos_t region_end = _region->position() + _region->length() - 1;
3043 if (end_frame > region_end) {
3045 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3047 _region->clear_changes ();
3048 _region->set_length (end_frame, this);
3049 trackview.session()->add_command (new StatefulDiffCommand (_region));
3054 trackview.session()->commit_reversible_command ();
3057 struct EventNoteTimeEarlyFirstComparator {
3058 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3059 return a->note()->time() < b->note()->time();
3064 MidiRegionView::time_sort_events ()
3066 if (!_sort_needed) {
3070 EventNoteTimeEarlyFirstComparator cmp;
3073 _sort_needed = false;
3077 MidiRegionView::goto_next_note ()
3079 // framepos_t pos = -1;
3080 bool use_next = false;
3082 if (_events.back()->selected()) {
3086 time_sort_events ();
3088 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3089 if ((*i)->selected()) {
3092 } else if (use_next) {
3094 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3099 /* use the first one */
3101 unique_select (_events.front());
3106 MidiRegionView::goto_previous_note ()
3108 // framepos_t pos = -1;
3109 bool use_next = false;
3111 if (_events.front()->selected()) {
3115 time_sort_events ();
3117 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3118 if ((*i)->selected()) {
3121 } else if (use_next) {
3123 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3128 /* use the last one */
3130 unique_select (*(_events.rbegin()));
3134 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3136 bool had_selected = false;
3138 time_sort_events ();
3140 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3141 if ((*i)->selected()) {
3142 selected.insert ((*i)->note());
3143 had_selected = true;
3147 if (allow_all_if_none_selected && !had_selected) {
3148 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3149 selected.insert ((*i)->note());
3155 MidiRegionView::update_ghost_note (double x, double y)
3160 _note_group->w2i (x, y);
3161 framepos_t const f = snap_pixel_to_frame (x);
3164 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
3170 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
3172 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
3173 _ghost_note->note()->set_length (length);
3174 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3176 /* the ghost note does not appear in ghost regions, so pass false in here */
3177 update_note (_ghost_note, false);
3179 show_verbose_cursor (_ghost_note->note ());
3183 MidiRegionView::create_ghost_note (double x, double y)
3188 boost::shared_ptr<NoteType> g (new NoteType);
3189 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3190 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3191 update_ghost_note (x, y);
3192 _ghost_note->show ();
3197 show_verbose_cursor (_ghost_note->note ());
3201 MidiRegionView::snap_changed ()
3207 create_ghost_note (_last_ghost_x, _last_ghost_y);
3211 MidiRegionView::drop_down_keys ()
3213 _mouse_state = None;
3217 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3219 double note = midi_stream_view()->y_to_note(y);
3221 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3223 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3225 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3226 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3227 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3228 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3233 bool add_mrv_selection = false;
3235 if (_selection.empty()) {
3236 add_mrv_selection = true;
3239 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3240 if (_selection.insert (*i).second) {
3241 (*i)->set_selected (true);
3245 if (add_mrv_selection) {
3246 PublicEditor& editor (trackview.editor());
3247 editor.get_selection().add (this);
3252 MidiRegionView::color_handler ()
3254 RegionView::color_handler ();
3256 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3257 (*i)->set_selected ((*i)->selected()); // will change color
3260 /* XXX probably more to do here */
3264 MidiRegionView::enable_display (bool yn)
3266 RegionView::enable_display (yn);
3273 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3275 if (_step_edit_cursor == 0) {
3276 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3278 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3279 _step_edit_cursor->property_y1() = 0;
3280 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3281 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3282 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3285 move_step_edit_cursor (pos);
3286 _step_edit_cursor->show ();
3290 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3292 _step_edit_cursor_position = pos;
3294 if (_step_edit_cursor) {
3295 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3296 _step_edit_cursor->property_x1() = pixel;
3297 set_step_edit_cursor_width (_step_edit_cursor_width);
3302 MidiRegionView::hide_step_edit_cursor ()
3304 if (_step_edit_cursor) {
3305 _step_edit_cursor->hide ();
3310 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3312 _step_edit_cursor_width = beats;
3314 if (_step_edit_cursor) {
3315 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
3319 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3320 * @param buf Data that has been recorded.
3321 * @param w Source that this data will end up in.
3324 MidiRegionView::data_recorded (boost::shared_ptr<MidiBuffer> buf, boost::weak_ptr<MidiSource> w)
3326 if (!_active_notes) {
3327 /* we aren't actively being recorded to */
3331 boost::shared_ptr<MidiSource> src = w.lock ();
3332 if (!src || src != midi_region()->midi_source()) {
3333 /* recorded data was not destined for our source */
3337 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3338 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3340 framepos_t back = max_framepos;
3342 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3343 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3344 assert (ev.buffer ());
3346 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3348 if (ev.type() == MIDI_CMD_NOTE_ON) {
3350 boost::shared_ptr<NoteType> note (
3351 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3354 add_note (note, true);
3356 /* fix up our note range */
3357 if (ev.note() < _current_range_min) {
3358 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3359 } else if (ev.note() > _current_range_max) {
3360 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3363 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3364 resolve_note (ev.note (), time_beats);
3370 midi_stream_view()->check_record_layers (region(), back);
3374 MidiRegionView::trim_front_starting ()
3376 /* Reparent the note group to the region view's parent, so that it doesn't change
3377 when the region view is trimmed.
3379 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3380 _temporary_note_group->move (group->property_x(), group->property_y());
3381 _note_group->reparent (*_temporary_note_group);
3385 MidiRegionView::trim_front_ending ()
3387 _note_group->reparent (*group);
3388 delete _temporary_note_group;
3389 _temporary_note_group = 0;
3391 if (_region->start() < 0) {
3392 /* Trim drag made start time -ve; fix this */
3393 midi_region()->fix_negative_start ();
3397 /** @return channel (counted from 0) to add an event to, based on the current setting
3398 * of the channel selector.
3401 MidiRegionView::get_channel_for_add () const
3403 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3404 uint16_t const chn_mask = mtv->channel_selector().get_selected_channels();
3406 uint8_t channel = 0;
3408 /* pick the highest selected channel, unless all channels are selected,
3409 which is interpreted to mean channel 1 (zero)
3412 for (uint16_t i = 0; i < 16; ++i) {
3413 if (chn_mask & (1<<i)) {
3419 if (chn_cnt == 16) {
3427 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3429 PatchChangeDialog d (&_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3430 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3434 change_patch_change (pc->patch(), d.patch ());
3439 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3442 snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3443 Evoral::midi_note_name (n->note()).c_str(),
3445 (int) n->channel() + 1,
3446 (int) n->velocity());
3448 show_verbose_cursor (buf, 10, 20);
3452 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3456 trackview.editor().get_pointer_position (wx, wy);
3461 trackview.editor().verbose_cursor()->set (text, wx, wy);
3462 trackview.editor().verbose_cursor()->show ();