2 Copyright (C) 2001-2007 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"
75 using namespace ARDOUR;
77 using namespace Editing;
78 using namespace ArdourCanvas;
79 using Gtkmm2ext::Keyboard;
81 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
82 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
83 : RegionView (parent, tv, r, spu, basic_color)
85 , _last_channel_selection(0xFFFF)
86 , _current_range_min(0)
87 , _current_range_max(0)
88 , _model_name(string())
89 , _custom_device_mode(string())
91 , _note_group(new ArdourCanvas::Group(*group))
92 , _note_diff_command (0)
95 , _step_edit_cursor (0)
96 , _step_edit_cursor_width (1.0)
97 , _step_edit_cursor_position (0.0)
98 , _channel_selection_scoped_note (0)
99 , _temporary_note_group (0)
102 , _sort_needed (true)
103 , _optimization_iterator (_events.end())
105 , no_sound_notes (false)
108 , pre_enter_cursor (0)
110 _note_group->raise_to_top();
111 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
113 connect_to_diskstream ();
116 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
117 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
118 TimeAxisViewItem::Visibility visibility)
119 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
121 , _last_channel_selection(0xFFFF)
122 , _model_name(string())
123 , _custom_device_mode(string())
125 , _note_group(new ArdourCanvas::Group(*parent))
126 , _note_diff_command (0)
129 , _step_edit_cursor (0)
130 , _step_edit_cursor_width (1.0)
131 , _step_edit_cursor_position (0.0)
132 , _channel_selection_scoped_note (0)
133 , _temporary_note_group (0)
136 , _sort_needed (true)
137 , _optimization_iterator (_events.end())
139 , no_sound_notes (false)
143 _note_group->raise_to_top();
144 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
146 connect_to_diskstream ();
149 MidiRegionView::MidiRegionView (const MidiRegionView& other)
150 : sigc::trackable(other)
153 , _last_channel_selection(0xFFFF)
154 , _model_name(string())
155 , _custom_device_mode(string())
157 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
158 , _note_diff_command (0)
161 , _step_edit_cursor (0)
162 , _step_edit_cursor_width (1.0)
163 , _step_edit_cursor_position (0.0)
164 , _channel_selection_scoped_note (0)
165 , _temporary_note_group (0)
168 , _sort_needed (true)
169 , _optimization_iterator (_events.end())
171 , no_sound_notes (false)
178 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
179 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
184 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
185 : RegionView (other, boost::shared_ptr<Region> (region))
187 , _last_channel_selection(0xFFFF)
188 , _model_name(string())
189 , _custom_device_mode(string())
191 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
192 , _note_diff_command (0)
195 , _step_edit_cursor (0)
196 , _step_edit_cursor_width (1.0)
197 , _step_edit_cursor_position (0.0)
198 , _channel_selection_scoped_note (0)
199 , _temporary_note_group (0)
202 , _sort_needed (true)
203 , _optimization_iterator (_events.end())
205 , no_sound_notes (false)
212 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
213 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
219 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
221 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
223 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
224 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
228 midi_region()->midi_source(0)->load_model();
231 _model = midi_region()->midi_source(0)->model();
232 _enable_display = false;
234 RegionView::init (basic_color, false);
236 compute_colors (basic_color);
238 set_height (trackview.current_height());
241 region_sync_changed ();
242 region_resized (ARDOUR::bounds_change);
245 reset_width_dependent_items (_pixel_width);
249 _enable_display = true;
252 display_model (_model);
256 group->raise_to_top();
257 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
259 midi_view()->signal_channel_mode_changed().connect(
260 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
262 midi_view()->signal_midi_patch_settings_changed().connect(
263 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
265 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
267 connect_to_diskstream ();
271 MidiRegionView::connect_to_diskstream ()
273 midi_view()->midi_track()->DataRecorded.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::data_recorded, this, _1, _2), gui_context ());
277 MidiRegionView::canvas_event(GdkEvent* ev)
280 case GDK_ENTER_NOTIFY:
281 case GDK_LEAVE_NOTIFY:
282 _last_event_x = ev->crossing.x;
283 _last_event_y = ev->crossing.y;
285 case GDK_MOTION_NOTIFY:
286 _last_event_x = ev->motion.x;
287 _last_event_y = ev->motion.y;
293 if (!trackview.editor().internal_editing()) {
299 return scroll (&ev->scroll);
302 return key_press (&ev->key);
304 case GDK_KEY_RELEASE:
305 return key_release (&ev->key);
307 case GDK_BUTTON_PRESS:
308 return button_press (&ev->button);
310 case GDK_2BUTTON_PRESS:
313 case GDK_BUTTON_RELEASE:
314 return button_release (&ev->button);
316 case GDK_ENTER_NOTIFY:
317 return enter_notify (&ev->crossing);
319 case GDK_LEAVE_NOTIFY:
320 return leave_notify (&ev->crossing);
322 case GDK_MOTION_NOTIFY:
323 return motion (&ev->motion);
333 MidiRegionView::remove_ghost_note ()
340 MidiRegionView::enter_notify (GdkEventCrossing* ev)
342 trackview.editor().MouseModeChanged.connect (
343 _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
346 Keyboard::magic_widget_grab_focus();
349 if (trackview.editor().current_mouse_mode() == MouseRange) {
350 create_ghost_note (ev->x, ev->y);
357 MidiRegionView::leave_notify (GdkEventCrossing*)
359 _mouse_mode_connection.disconnect ();
361 trackview.editor().hide_verbose_canvas_cursor ();
362 remove_ghost_note ();
367 MidiRegionView::mouse_mode_changed ()
369 if (trackview.editor().current_mouse_mode() == MouseRange && trackview.editor().internal_editing()) {
370 create_ghost_note (_last_event_x, _last_event_y);
372 remove_ghost_note ();
373 trackview.editor().hide_verbose_canvas_cursor ();
378 MidiRegionView::button_press (GdkEventButton* ev)
380 if (ev->button != 1) {
387 group->w2i (_last_x, _last_y);
389 if (_mouse_state != SelectTouchDragging) {
391 _pressed_button = ev->button;
392 _mouse_state = Pressed;
397 _pressed_button = ev->button;
403 MidiRegionView::button_release (GdkEventButton* ev)
405 double event_x, event_y;
406 framepos_t event_frame = 0;
408 if (ev->button != 1) {
415 group->w2i(event_x, event_y);
416 group->ungrab(ev->time);
418 event_frame = trackview.editor().pixel_to_frame(event_x);
420 switch (_mouse_state) {
421 case Pressed: // Clicked
423 switch (trackview.editor().current_mouse_mode()) {
429 if (Keyboard::is_insert_note_event(ev)){
431 double event_x, event_y;
435 group->w2i(event_x, event_y);
438 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
444 create_note_at (event_x, event_y, beats, true);
452 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
458 create_note_at (event_x, event_y, beats, true);
469 case SelectRectDragging: // Select drag done
476 case AddDragging: // Add drag done
480 if (Keyboard::is_insert_note_event(ev) || trackview.editor().current_mouse_mode() == MouseRange){
482 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
484 const double x = _drag_rect->property_x1();
485 const double length = trackview.editor().pixel_to_frame (_drag_rect->property_x2() - _drag_rect->property_x1());
487 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), true);
494 create_ghost_note (ev->x, ev->y);
504 MidiRegionView::motion (GdkEventMotion* ev)
506 double event_x, event_y;
507 framepos_t event_frame = 0;
511 group->w2i(event_x, event_y);
513 // convert event_x to global frame
514 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
515 trackview.editor().snap_to(event_frame);
517 // convert event_frame back to local coordinates relative to position
518 event_frame -= _region->position();
520 if (!_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
521 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())
522 && _mouse_state != AddDragging){
524 create_ghost_note (ev->x, ev->y);
526 else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
527 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())){
529 update_ghost_note (ev->x, ev->y);
531 else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange){
536 trackview.editor().hide_verbose_canvas_cursor ();
538 else if (_ghost_note && trackview.editor().current_mouse_mode() == MouseRange) {
539 update_ghost_note (ev->x, ev->y);
542 /* any motion immediately hides velocity text that may have been visible */
544 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
545 (*i)->hide_velocity ();
548 switch (_mouse_state) {
549 case Pressed: // Maybe start a drag, if we've moved a bit
551 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
552 /* no appreciable movement since the button was pressed */
557 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject
558 && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
560 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
561 Gdk::Cursor(Gdk::FLEUR), ev->time);
565 _drag_start_x = event_x;
566 _drag_start_y = event_y;
568 _drag_rect = new ArdourCanvas::SimpleRect(*group);
569 _drag_rect->property_x1() = event_x;
570 _drag_rect->property_y1() = event_y;
571 _drag_rect->property_x2() = event_x;
572 _drag_rect->property_y2() = event_y;
573 _drag_rect->property_outline_what() = 0xFF;
574 _drag_rect->property_outline_color_rgba()
575 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
576 _drag_rect->property_fill_color_rgba()
577 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
579 _mouse_state = SelectRectDragging;
582 // Add note drag start
583 } else if (trackview.editor().internal_editing()) {
588 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
589 Gdk::Cursor(Gdk::FLEUR), ev->time);
593 _drag_start_x = event_x;
594 _drag_start_y = event_y;
596 _drag_rect = new ArdourCanvas::SimpleRect(*group);
597 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
599 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
600 midi_stream_view()->y_to_note(event_y));
601 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
602 _drag_rect->property_y2() = _drag_rect->property_y1()
603 + floor(midi_stream_view()->note_height());
604 _drag_rect->property_outline_what() = 0xFF;
605 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
606 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
608 _mouse_state = AddDragging;
615 trackview.editor().hide_verbose_canvas_cursor ();
623 case SelectRectDragging: // Select drag motion
624 case AddDragging: // Add note drag motion
629 GdkModifierType state;
630 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
635 if (_mouse_state == AddDragging){
636 event_x = trackview.editor().frame_to_pixel(event_frame);
641 if (event_x > _drag_start_x){
642 _drag_rect->property_x2() = event_x;
645 _drag_rect->property_x1() = event_x;
649 if (_drag_rect && _mouse_state == SelectRectDragging) {
651 if (event_y > _drag_start_y){
652 _drag_rect->property_y2() = event_y;
655 _drag_rect->property_y1() = event_y;
658 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
664 case SelectTouchDragging:
676 MidiRegionView::scroll (GdkEventScroll* ev)
678 if (_selection.empty()) {
682 trackview.editor().hide_verbose_canvas_cursor ();
684 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
686 if (ev->direction == GDK_SCROLL_UP) {
687 change_velocities (true, fine, false);
688 } else if (ev->direction == GDK_SCROLL_DOWN) {
689 change_velocities (false, fine, false);
695 MidiRegionView::key_press (GdkEventKey* ev)
697 /* since GTK bindings are generally activated on press, and since
698 detectable auto-repeat is the name of the game and only sends
699 repeated presses, carry out key actions at key press, not release.
702 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
703 _mouse_state = SelectTouchDragging;
706 } else if (ev->keyval == GDK_Escape) {
710 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
712 bool start = (ev->keyval == GDK_comma);
713 bool end = (ev->keyval == GDK_period);
714 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
715 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
717 change_note_lengths (fine, shorter, 0.0, start, end);
721 } else if (ev->keyval == GDK_Delete) {
726 } else if (ev->keyval == GDK_Tab) {
728 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
729 goto_previous_note ();
735 } else if (ev->keyval == GDK_Up) {
737 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
738 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
740 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
741 change_velocities (true, fine, allow_smush);
743 transpose (true, fine, allow_smush);
747 } else if (ev->keyval == GDK_Down) {
749 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
750 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
752 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
753 change_velocities (false, fine, allow_smush);
755 transpose (false, fine, allow_smush);
759 } else if (ev->keyval == GDK_Left) {
764 } else if (ev->keyval == GDK_Right) {
769 } else if (ev->keyval == GDK_Control_L) {
778 MidiRegionView::key_release (GdkEventKey* ev)
780 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
788 MidiRegionView::show_list_editor ()
791 _list_editor = new MidiListEditor (trackview.session(), midi_region());
793 _list_editor->present ();
796 /** Add a note to the model, and the view, at a canvas (click) coordinate.
797 * \param x horizontal position in pixels
798 * \param y vertical position in pixels
799 * \param length duration of the note in beats, which will be snapped to the grid
800 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
803 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
805 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
806 MidiStreamView* const view = mtv->midi_view();
808 double note = view->y_to_note(y);
811 assert(note <= 127.0);
813 // Start of note in frames relative to region start
814 framepos_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
815 assert(start_frames >= 0);
818 length = frames_to_beats(
819 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
821 assert (length != 0);
824 length = frames_to_beats (beats_to_frames (length) - 1);
827 const boost::shared_ptr<NoteType> new_note (new NoteType (get_channel_for_add (),
828 frames_to_beats(start_frames + _region->start()), length,
829 (uint8_t)note, 0x40));
831 if (_model->contains (new_note)) {
835 view->update_note_range(new_note->note());
837 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note");
839 _model->apply_command(*trackview.session(), cmd);
841 play_midi_note (new_note);
845 MidiRegionView::clear_events()
850 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
851 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
856 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
861 _patch_changes.clear();
863 _optimization_iterator = _events.end();
867 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
871 content_connection.disconnect ();
872 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
876 if (_enable_display) {
882 MidiRegionView::start_note_diff_command (string name)
884 if (!_note_diff_command) {
885 _note_diff_command = _model->new_note_diff_command (name);
890 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
892 if (_note_diff_command) {
893 _note_diff_command->add (note);
896 _marked_for_selection.insert(note);
899 _marked_for_velocity.insert(note);
904 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
906 if (_note_diff_command && ev->note()) {
907 _note_diff_command->remove(ev->note());
912 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
913 MidiModel::NoteDiffCommand::Property property,
916 if (_note_diff_command) {
917 _note_diff_command->change (ev->note(), property, val);
922 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
923 MidiModel::NoteDiffCommand::Property property,
924 Evoral::MusicalTime val)
926 if (_note_diff_command) {
927 _note_diff_command->change (ev->note(), property, val);
932 MidiRegionView::apply_diff (bool as_subcommand)
936 if (!_note_diff_command) {
940 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
941 // Mark all selected notes for selection when model reloads
942 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
943 _marked_for_selection.insert((*i)->note());
948 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
950 _model->apply_command (*trackview.session(), _note_diff_command);
953 _note_diff_command = 0;
954 midi_view()->midi_track()->playlist_modified();
957 _marked_for_selection.clear();
960 _marked_for_velocity.clear();
964 MidiRegionView::abort_command()
966 delete _note_diff_command;
967 _note_diff_command = 0;
972 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
974 if (_optimization_iterator != _events.end()) {
975 ++_optimization_iterator;
978 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
979 return *_optimization_iterator;
982 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
983 if ((*_optimization_iterator)->note() == note) {
984 return *_optimization_iterator;
992 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
994 MidiModel::Notes notes;
995 _model->get_notes (notes, op, val, chan_mask);
997 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
998 CanvasNoteEvent* cne = find_canvas_note (*n);
1006 MidiRegionView::redisplay_model()
1008 // Don't redisplay the model if we're currently recording and displaying that
1009 if (_active_notes) {
1017 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1018 (*i)->invalidate ();
1021 MidiModel::ReadLock lock(_model->read_lock());
1023 MidiModel::Notes& notes (_model->notes());
1024 _optimization_iterator = _events.begin();
1026 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1028 boost::shared_ptr<NoteType> note (*n);
1029 CanvasNoteEvent* cne;
1032 if (note_in_region_range (note, visible)) {
1034 if ((cne = find_canvas_note (note)) != 0) {
1041 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1043 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1055 add_note (note, visible);
1060 if ((cne = find_canvas_note (note)) != 0) {
1068 /* remove note items that are no longer valid */
1070 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1071 if (!(*i)->valid ()) {
1073 i = _events.erase (i);
1079 _patch_changes.clear();
1083 display_patch_changes ();
1085 _marked_for_selection.clear ();
1086 _marked_for_velocity.clear ();
1088 /* we may have caused _events to contain things out of order (e.g. if a note
1089 moved earlier or later). we don't generally need them in time order, but
1090 make a note that a sort is required for those cases that require it.
1093 _sort_needed = true;
1097 MidiRegionView::display_patch_changes ()
1099 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1100 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1102 for (uint8_t i = 0; i < 16; ++i) {
1103 if (chn_mask & (1<<i)) {
1104 display_patch_changes_on_channel (i);
1110 MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
1112 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1114 if ((*i)->channel() != channel) {
1118 MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
1120 boost::shared_ptr<MIDI::Name::Patch> patch =
1121 MIDI::Name::MidiPatchManager::instance().find_patch(
1122 _model_name, _custom_device_mode, channel, patch_key);
1125 add_canvas_patch_change (*i, patch->name());
1128 /* program and bank numbers are zero-based: convert to one-based */
1129 snprintf (buf, 16, "%d\n%d", (*i)->program() + 1, (*i)->bank() + 1);
1130 add_canvas_patch_change (*i, buf);
1136 MidiRegionView::display_sysexes()
1138 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1139 Evoral::MusicalTime time = (*i)->time();
1144 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1145 str << int((*i)->buffer()[b]);
1146 if (b != (*i)->size() -1) {
1150 string text = str.str();
1152 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1154 double height = midi_stream_view()->contents_height();
1156 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1157 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1159 // Show unless patch change is beyond the region bounds
1160 if (time - _region->start() >= _region->length() || time < _region->start()) {
1166 _sys_exes.push_back(sysex);
1171 MidiRegionView::~MidiRegionView ()
1173 in_destructor = true;
1175 trackview.editor().hide_verbose_canvas_cursor ();
1177 note_delete_connection.disconnect ();
1179 delete _list_editor;
1181 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1183 if (_active_notes) {
1191 delete _note_diff_command;
1192 delete _step_edit_cursor;
1193 delete _temporary_note_group;
1197 MidiRegionView::region_resized (const PropertyChange& what_changed)
1199 RegionView::region_resized(what_changed);
1201 if (what_changed.contains (ARDOUR::Properties::position)) {
1202 set_duration(_region->length(), 0);
1203 if (_enable_display) {
1210 MidiRegionView::reset_width_dependent_items (double pixel_width)
1212 RegionView::reset_width_dependent_items(pixel_width);
1213 assert(_pixel_width == pixel_width);
1215 if (_enable_display) {
1219 move_step_edit_cursor (_step_edit_cursor_position);
1220 set_step_edit_cursor_width (_step_edit_cursor_width);
1224 MidiRegionView::set_height (double height)
1226 static const double FUDGE = 2.0;
1227 const double old_height = _height;
1228 RegionView::set_height(height);
1229 _height = height - FUDGE;
1231 apply_note_range(midi_stream_view()->lowest_note(),
1232 midi_stream_view()->highest_note(),
1233 height != old_height + FUDGE);
1236 name_pixbuf->raise_to_top();
1239 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1240 (*x)->set_height (midi_stream_view()->contents_height());
1243 if (_step_edit_cursor) {
1244 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1249 /** Apply the current note range from the stream view
1250 * by repositioning/hiding notes as necessary
1253 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1255 if (!_enable_display) {
1259 if (!force && _current_range_min == min && _current_range_max == max) {
1263 _current_range_min = min;
1264 _current_range_max = max;
1266 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1267 CanvasNoteEvent* event = *i;
1268 boost::shared_ptr<NoteType> note (event->note());
1270 if (note->note() < _current_range_min ||
1271 note->note() > _current_range_max) {
1277 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1279 const double y1 = midi_stream_view()->note_to_y(note->note());
1280 const double y2 = y1 + floor(midi_stream_view()->note_height());
1282 cnote->property_y1() = y1;
1283 cnote->property_y2() = y2;
1285 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1287 const double diamond_size = update_hit (chit);
1289 chit->set_height (diamond_size);
1295 MidiRegionView::add_ghost (TimeAxisView& tv)
1299 double unit_position = _region->position () / samples_per_unit;
1300 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1301 MidiGhostRegion* ghost;
1303 if (mtv && mtv->midi_view()) {
1304 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1305 to allow having midi notes on top of note lines and waveforms.
1307 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1309 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1312 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1313 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1314 ghost->add_note(note);
1318 ghost->set_height ();
1319 ghost->set_duration (_region->length() / samples_per_unit);
1320 ghosts.push_back (ghost);
1322 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1328 /** Begin tracking note state for successive calls to add_event
1331 MidiRegionView::begin_write()
1333 assert(!_active_notes);
1334 _active_notes = new CanvasNote*[128];
1335 for (unsigned i=0; i < 128; ++i) {
1336 _active_notes[i] = 0;
1341 /** Destroy note state for add_event
1344 MidiRegionView::end_write()
1346 delete[] _active_notes;
1348 _marked_for_selection.clear();
1349 _marked_for_velocity.clear();
1353 /** Resolve an active MIDI note (while recording).
1356 MidiRegionView::resolve_note(uint8_t note, double end_time)
1358 if (midi_view()->note_mode() != Sustained) {
1362 if (_active_notes && _active_notes[note]) {
1364 const framepos_t end_time_frames = beats_to_frames(end_time);
1366 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1367 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1368 _active_notes[note] = 0;
1373 /** Extend active notes to rightmost edge of region (if length is changed)
1376 MidiRegionView::extend_active_notes()
1378 if (!_active_notes) {
1382 for (unsigned i=0; i < 128; ++i) {
1383 if (_active_notes[i]) {
1384 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1391 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1393 if (no_sound_notes || !trackview.editor().sound_notes()) {
1397 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1399 if (!route_ui || !route_ui->midi_track()) {
1403 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1409 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1411 if (no_sound_notes || !trackview.editor().sound_notes()) {
1415 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1417 if (!route_ui || !route_ui->midi_track()) {
1421 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1423 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1432 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1434 const framepos_t note_start_frames = beats_to_frames(note->time());
1436 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1437 (note_start_frames < _region->start());
1439 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1440 (note->note() <= midi_stream_view()->highest_note());
1445 /** Update a canvas note's size from its model note.
1446 * @param ev Canvas note to update.
1447 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1450 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1452 boost::shared_ptr<NoteType> note = ev->note();
1454 const framepos_t note_start_frames = beats_to_frames(note->time());
1456 /* trim note display to not overlap the end of its region */
1457 const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1459 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1460 const double y1 = midi_stream_view()->note_to_y(note->note());
1461 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1463 ev->property_x1() = x;
1464 ev->property_y1() = y1;
1466 if (note->length() > 0) {
1467 ev->property_x2() = note_endpixel;
1469 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1472 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1474 if (note->length() == 0) {
1475 if (_active_notes) {
1476 assert(note->note() < 128);
1477 // If this note is already active there's a stuck note,
1478 // finish the old note rectangle
1479 if (_active_notes[note->note()]) {
1480 CanvasNote* const old_rect = _active_notes[note->note()];
1481 boost::shared_ptr<NoteType> old_note = old_rect->note();
1482 old_rect->property_x2() = x;
1483 old_rect->property_outline_what() = (guint32) 0xF;
1485 _active_notes[note->note()] = ev;
1487 /* outline all but right edge */
1488 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1490 /* outline all edges */
1491 ev->property_outline_what() = (guint32) 0xF;
1494 if (update_ghost_regions) {
1495 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1496 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1498 gr->update_note (ev);
1505 MidiRegionView::update_hit (CanvasHit* ev)
1507 boost::shared_ptr<NoteType> note = ev->note();
1509 const framepos_t note_start_frames = beats_to_frames(note->time());
1510 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1511 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1512 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1516 return diamond_size;
1519 /** Add a MIDI note to the view (with length).
1521 * If in sustained mode, notes with length 0 will be considered active
1522 * notes, and resolve_note should be called when the corresponding note off
1523 * event arrives, to properly display the note.
1526 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1528 CanvasNoteEvent* event = 0;
1530 assert(note->time() >= 0);
1531 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1533 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1535 if (midi_view()->note_mode() == Sustained) {
1537 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1539 update_note (ev_rect);
1543 MidiGhostRegion* gr;
1545 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1546 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1547 gr->add_note(ev_rect);
1551 } else if (midi_view()->note_mode() == Percussive) {
1553 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1555 CanvasHit* ev_diamond = new CanvasHit(*this, *_note_group, diamond_size, note);
1557 update_hit (ev_diamond);
1566 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1567 note_selected(event, true);
1570 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1571 event->show_velocity();
1574 event->on_channel_selection_change(_last_channel_selection);
1575 _events.push_back(event);
1584 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1585 MidiStreamView* const view = mtv->midi_view();
1587 view->update_note_range(note->note());
1591 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1592 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1594 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1596 /* potentially extend region to hold new note */
1598 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1599 framepos_t region_end = _region->position() + _region->length() - 1;
1601 if (end_frame > region_end) {
1602 _region->set_length (end_frame - _region->position(), this);
1605 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1606 MidiStreamView* const view = mtv->midi_view();
1608 view->update_note_range(new_note->note());
1610 _marked_for_selection.clear ();
1613 start_note_diff_command (_("step add"));
1614 note_diff_add_note (new_note, true, false);
1617 // last_step_edit_note = new_note;
1621 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1623 change_note_lengths (false, false, beats, false, true);
1627 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
1629 assert (patch->time() >= 0);
1631 const double x = trackview.editor().frame_to_pixel (beats_to_frames (patch->time()));
1633 double const height = midi_stream_view()->contents_height();
1635 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1636 new CanvasPatchChange(*this, *_note_group,
1641 _custom_device_mode,
1645 // Show unless patch change is beyond the region bounds
1646 if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1647 patch_change->hide();
1649 patch_change->show();
1652 _patch_changes.push_back (patch_change);
1656 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1658 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1659 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1663 if (i != _model->patch_changes().end()) {
1664 key.msb = (*i)->bank_msb ();
1665 key.lsb = (*i)->bank_lsb ();
1666 key.program_number = (*i)->program ();
1668 key.msb = key.lsb = key.program_number = 0;
1671 assert (key.is_sane());
1676 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1678 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1680 if (pc.patch()->program() != new_patch.program_number) {
1681 c->change_program (pc.patch (), new_patch.program_number);
1684 int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1685 if (pc.patch()->bank() != new_bank) {
1686 c->change_bank (pc.patch (), new_bank);
1689 _model->apply_command (*trackview.session(), c);
1691 _patch_changes.clear ();
1692 display_patch_changes ();
1696 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1698 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1700 if (old_change->time() != new_change.time()) {
1701 c->change_time (old_change, new_change.time());
1704 if (old_change->channel() != new_change.channel()) {
1705 c->change_channel (old_change, new_change.channel());
1708 if (old_change->program() != new_change.program()) {
1709 c->change_program (old_change, new_change.program());
1712 if (old_change->bank() != new_change.bank()) {
1713 c->change_bank (old_change, new_change.bank());
1716 _model->apply_command (*trackview.session(), c);
1718 _patch_changes.clear ();
1719 display_patch_changes ();
1722 /** Add a patch change to the region.
1723 * @param t Time in frames relative to region position
1724 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1725 * get_channel_for_add())
1728 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1730 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1731 c->add (MidiModel::PatchChangePtr (
1732 new Evoral::PatchChange<Evoral::MusicalTime> (
1733 frames_to_beats (t + midi_region()->start()), get_channel_for_add(), patch.program(), patch.bank()
1737 _model->apply_command (*trackview.session(), c);
1739 _patch_changes.clear ();
1740 display_patch_changes ();
1744 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1746 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1747 c->change_time (pc.patch (), t);
1748 _model->apply_command (*trackview.session(), c);
1750 _patch_changes.clear ();
1751 display_patch_changes ();
1755 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1757 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1758 c->remove (pc->patch ());
1759 _model->apply_command (*trackview.session(), c);
1761 _patch_changes.clear ();
1762 display_patch_changes ();
1766 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1768 if (patch.patch()->program() < 127) {
1769 MIDI::Name::PatchPrimaryKey key;
1770 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1771 key.program_number++;
1772 change_patch_change (patch, key);
1777 MidiRegionView::next_patch (CanvasPatchChange& patch)
1779 if (patch.patch()->program() > 0) {
1780 MIDI::Name::PatchPrimaryKey key;
1781 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1782 key.program_number--;
1783 change_patch_change (patch, key);
1788 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1790 if (_selection.empty()) {
1794 if (_selection.erase (cne) > 0) {
1795 cerr << "Erased a CNE from selection\n";
1800 MidiRegionView::delete_selection()
1802 if (_selection.empty()) {
1806 start_note_diff_command (_("delete selection"));
1808 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1809 if ((*i)->selected()) {
1810 _note_diff_command->remove((*i)->note());
1820 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1822 start_note_diff_command (_("delete note"));
1823 _note_diff_command->remove (n);
1826 trackview.editor().hide_verbose_canvas_cursor ();
1830 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1832 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1833 if ((*i)->selected() && (*i) != ev) {
1834 (*i)->set_selected(false);
1835 (*i)->hide_velocity();
1843 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1845 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1848 Selection::iterator tmp = i;
1851 (*i)->set_selected (false);
1852 _selection.erase (i);
1861 /* don't bother with removing this regionview from the editor selection,
1862 since we're about to add another note, and thus put/keep this
1863 regionview in the editor selection.
1866 if (!ev->selected()) {
1867 add_to_selection (ev);
1872 MidiRegionView::select_all_notes ()
1876 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1877 add_to_selection (*i);
1882 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1884 uint8_t low_note = 127;
1885 uint8_t high_note = 0;
1886 MidiModel::Notes& notes (_model->notes());
1887 _optimization_iterator = _events.begin();
1893 if (extend && _selection.empty()) {
1899 /* scan existing selection to get note range */
1901 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1902 if ((*i)->note()->note() < low_note) {
1903 low_note = (*i)->note()->note();
1905 if ((*i)->note()->note() > high_note) {
1906 high_note = (*i)->note()->note();
1910 low_note = min (low_note, notenum);
1911 high_note = max (high_note, notenum);
1914 no_sound_notes = true;
1916 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1918 boost::shared_ptr<NoteType> note (*n);
1919 CanvasNoteEvent* cne;
1920 bool select = false;
1922 if (((1 << note->channel()) & channel_mask) != 0) {
1924 if ((note->note() >= low_note && note->note() <= high_note)) {
1927 } else if (note->note() == notenum) {
1933 if ((cne = find_canvas_note (note)) != 0) {
1934 // extend is false because we've taken care of it,
1935 // since it extends by time range, not pitch.
1936 note_selected (cne, add, false);
1940 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1944 no_sound_notes = false;
1948 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1950 MidiModel::Notes& notes (_model->notes());
1951 _optimization_iterator = _events.begin();
1953 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1955 boost::shared_ptr<NoteType> note (*n);
1956 CanvasNoteEvent* cne;
1958 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1959 if ((cne = find_canvas_note (note)) != 0) {
1960 if (cne->selected()) {
1961 note_deselected (cne);
1963 note_selected (cne, true, false);
1971 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1974 clear_selection_except(ev);
1979 if (!ev->selected()) {
1980 add_to_selection (ev);
1984 /* find end of latest note selected, select all between that and the start of "ev" */
1986 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1987 Evoral::MusicalTime latest = 0;
1989 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1990 if ((*i)->note()->end_time() > latest) {
1991 latest = (*i)->note()->end_time();
1993 if ((*i)->note()->time() < earliest) {
1994 earliest = (*i)->note()->time();
1998 if (ev->note()->end_time() > latest) {
1999 latest = ev->note()->end_time();
2002 if (ev->note()->time() < earliest) {
2003 earliest = ev->note()->time();
2006 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2008 /* find notes entirely within OR spanning the earliest..latest range */
2010 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2011 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2012 add_to_selection (*i);
2016 /* if events were guaranteed to be time sorted, we could do this.
2017 but as of sept 10th 2009, they no longer are.
2020 if ((*i)->note()->time() > latest) {
2029 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2031 remove_from_selection (ev);
2035 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
2045 // TODO: Make this faster by storing the last updated selection rect, and only
2046 // adjusting things that are in the area that appears/disappeared.
2047 // We probably need a tree to be able to find events in O(log(n)) time.
2049 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2051 /* check if any corner of the note is inside the rect
2054 1) this is computing "touched by", not "contained by" the rect.
2055 2) this does not require that events be sorted in time.
2058 const double ix1 = (*i)->x1();
2059 const double ix2 = (*i)->x2();
2060 const double iy1 = (*i)->y1();
2061 const double iy2 = (*i)->y2();
2063 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2064 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2065 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2066 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2069 if (!(*i)->selected()) {
2070 add_to_selection (*i);
2072 } else if ((*i)->selected()) {
2073 // Not inside rectangle
2074 remove_from_selection (*i);
2080 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2082 Selection::iterator i = _selection.find (ev);
2084 if (i != _selection.end()) {
2085 _selection.erase (i);
2088 ev->set_selected (false);
2089 ev->hide_velocity ();
2091 if (_selection.empty()) {
2092 PublicEditor& editor (trackview.editor());
2093 editor.get_selection().remove (this);
2098 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2100 bool add_mrv_selection = false;
2102 if (_selection.empty()) {
2103 add_mrv_selection = true;
2106 if (_selection.insert (ev).second) {
2107 ev->set_selected (true);
2108 play_midi_note ((ev)->note());
2111 if (add_mrv_selection) {
2112 PublicEditor& editor (trackview.editor());
2113 editor.get_selection().add (this);
2118 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2120 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2121 PossibleChord to_play;
2122 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2124 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2125 if ((*i)->note()->time() < earliest) {
2126 earliest = (*i)->note()->time();
2130 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2131 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2132 to_play.push_back ((*i)->note());
2134 (*i)->move_event(dx, dy);
2137 if (dy && !_selection.empty() && !no_sound_notes && trackview.editor().sound_notes()) {
2139 if (to_play.size() > 1) {
2141 PossibleChord shifted;
2143 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2144 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2145 moved_note->set_note (moved_note->note() + cumulative_dy);
2146 shifted.push_back (moved_note);
2149 play_midi_chord (shifted);
2151 } else if (!to_play.empty()) {
2153 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2154 moved_note->set_note (moved_note->note() + cumulative_dy);
2155 play_midi_note (moved_note);
2161 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2163 assert (!_selection.empty());
2165 uint8_t lowest_note_in_selection = 127;
2166 uint8_t highest_note_in_selection = 0;
2167 uint8_t highest_note_difference = 0;
2169 // find highest and lowest notes first
2171 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2172 uint8_t pitch = (*i)->note()->note();
2173 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2174 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2178 cerr << "dnote: " << (int) dnote << endl;
2179 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2180 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2181 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2182 << int(highest_note_in_selection) << endl;
2183 cerr << "selection size: " << _selection.size() << endl;
2184 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2187 // Make sure the note pitch does not exceed the MIDI standard range
2188 if (highest_note_in_selection + dnote > 127) {
2189 highest_note_difference = highest_note_in_selection - 127;
2192 start_note_diff_command (_("move notes"));
2194 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2196 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2202 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2204 uint8_t original_pitch = (*i)->note()->note();
2205 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2207 // keep notes in standard midi range
2208 clamp_to_0_127(new_pitch);
2210 // keep original pitch if note is dragged outside valid midi range
2211 if ((original_pitch != 0 && new_pitch == 0)
2212 || (original_pitch != 127 && new_pitch == 127)) {
2213 new_pitch = original_pitch;
2216 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2217 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2219 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2224 // care about notes being moved beyond the upper/lower bounds on the canvas
2225 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2226 highest_note_in_selection > midi_stream_view()->highest_note()) {
2227 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2232 MidiRegionView::snap_pixel_to_frame(double x)
2234 PublicEditor& editor = trackview.editor();
2235 // x is region relative, convert it to global absolute frames
2236 framepos_t frame = editor.pixel_to_frame(x) + _region->position();
2237 editor.snap_to(frame);
2238 return frame - _region->position(); // convert back to region relative
2242 MidiRegionView::snap_frame_to_frame(framepos_t x)
2244 PublicEditor& editor = trackview.editor();
2245 // x is region relative, convert it to global absolute frames
2246 framepos_t frame = x + _region->position();
2247 editor.snap_to(frame);
2248 return frame - _region->position(); // convert back to region relative
2252 MidiRegionView::snap_to_pixel(double x)
2254 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2258 MidiRegionView::get_position_pixels()
2260 framepos_t region_frame = get_position();
2261 return trackview.editor().frame_to_pixel(region_frame);
2265 MidiRegionView::get_end_position_pixels()
2267 framepos_t frame = get_position() + get_duration ();
2268 return trackview.editor().frame_to_pixel(frame);
2272 MidiRegionView::beats_to_frames(double beats) const
2274 return _time_converter.to(beats);
2278 MidiRegionView::frames_to_beats(framepos_t frames) const
2280 return _time_converter.from(frames);
2284 MidiRegionView::begin_resizing (bool /*at_front*/)
2286 _resize_data.clear();
2288 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2289 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2291 // only insert CanvasNotes into the map
2293 NoteResizeData *resize_data = new NoteResizeData();
2294 resize_data->canvas_note = note;
2296 // create a new SimpleRect from the note which will be the resize preview
2297 SimpleRect *resize_rect = new SimpleRect(
2298 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2300 // calculate the colors: get the color settings
2301 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2302 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2305 // make the resize preview notes more transparent and bright
2306 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2308 // calculate color based on note velocity
2309 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2310 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2314 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2315 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2317 resize_data->resize_rect = resize_rect;
2318 _resize_data.push_back(resize_data);
2323 /** Update resizing notes while user drags.
2324 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2325 * @param at_front which end of the note (true == note on, false == note off)
2326 * @param delta_x change in mouse position since the start of the drag
2327 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2328 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2329 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2330 * as the \a primary note.
2333 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2335 bool cursor_set = false;
2337 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2338 SimpleRect* resize_rect = (*i)->resize_rect;
2339 CanvasNote* canvas_note = (*i)->canvas_note;
2344 current_x = canvas_note->x1() + delta_x;
2346 current_x = primary->x1() + delta_x;
2350 current_x = canvas_note->x2() + delta_x;
2352 current_x = primary->x2() + delta_x;
2357 resize_rect->property_x1() = snap_to_pixel(current_x);
2358 resize_rect->property_x2() = canvas_note->x2();
2360 resize_rect->property_x2() = snap_to_pixel(current_x);
2361 resize_rect->property_x1() = canvas_note->x1();
2367 beats = snap_pixel_to_frame (current_x);
2368 beats = frames_to_beats (beats);
2373 if (beats < canvas_note->note()->end_time()) {
2374 len = canvas_note->note()->time() - beats;
2375 len += canvas_note->note()->length();
2380 if (beats >= canvas_note->note()->time()) {
2381 len = beats - canvas_note->note()->time();
2388 snprintf (buf, sizeof (buf), "%.3g beats", len);
2389 trackview.editor().show_verbose_canvas_cursor_with (buf);
2398 /** Finish resizing notes when the user releases the mouse button.
2399 * Parameters the same as for \a update_resizing().
2402 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2404 start_note_diff_command (_("resize notes"));
2406 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2407 CanvasNote* canvas_note = (*i)->canvas_note;
2408 SimpleRect* resize_rect = (*i)->resize_rect;
2413 current_x = canvas_note->x1() + delta_x;
2415 current_x = primary->x1() + delta_x;
2419 current_x = canvas_note->x2() + delta_x;
2421 current_x = primary->x2() + delta_x;
2425 current_x = snap_pixel_to_frame (current_x);
2426 current_x = frames_to_beats (current_x);
2428 if (at_front && current_x < canvas_note->note()->end_time()) {
2429 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2431 double len = canvas_note->note()->time() - current_x;
2432 len += canvas_note->note()->length();
2435 /* XXX convert to beats */
2436 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2441 double len = current_x - canvas_note->note()->time();
2444 /* XXX convert to beats */
2445 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2453 _resize_data.clear();
2458 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2460 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, (uint8_t) channel);
2464 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2466 uint8_t new_velocity;
2469 new_velocity = event->note()->velocity() + velocity;
2470 clamp_to_0_127(new_velocity);
2472 new_velocity = velocity;
2475 event->set_selected (event->selected()); // change color
2477 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2481 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2486 new_note = event->note()->note() + note;
2491 clamp_to_0_127 (new_note);
2492 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2496 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2498 bool change_start = false;
2499 bool change_length = false;
2500 Evoral::MusicalTime new_start = 0;
2501 Evoral::MusicalTime new_length = 0;
2503 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2505 front_delta: if positive - move the start of the note later in time (shortening it)
2506 if negative - move the start of the note earlier in time (lengthening it)
2508 end_delta: if positive - move the end of the note later in time (lengthening it)
2509 if negative - move the end of the note earlier in time (shortening it)
2513 if (front_delta < 0) {
2515 if (event->note()->time() < -front_delta) {
2518 new_start = event->note()->time() + front_delta; // moves earlier
2521 /* start moved toward zero, so move the end point out to where it used to be.
2522 Note that front_delta is negative, so this increases the length.
2525 new_length = event->note()->length() - front_delta;
2526 change_start = true;
2527 change_length = true;
2531 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2533 if (new_pos < event->note()->end_time()) {
2534 new_start = event->note()->time() + front_delta;
2535 /* start moved toward the end, so move the end point back to where it used to be */
2536 new_length = event->note()->length() - front_delta;
2537 change_start = true;
2538 change_length = true;
2545 bool can_change = true;
2546 if (end_delta < 0) {
2547 if (event->note()->length() < -end_delta) {
2553 new_length = event->note()->length() + end_delta;
2554 change_length = true;
2559 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2562 if (change_length) {
2563 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2568 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2570 Evoral::MusicalTime new_time;
2574 if (event->note()->time() < -delta) {
2577 new_time = event->note()->time() + delta;
2580 new_time = event->note()->time() + delta;
2586 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2590 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2592 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2596 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2600 if (_selection.empty()) {
2615 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2616 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2622 start_note_diff_command (_("change velocities"));
2624 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2625 Selection::iterator next = i;
2627 change_note_velocity (*i, delta, true);
2633 if (!_selection.empty()) {
2635 snprintf (buf, sizeof (buf), "Vel %d",
2636 (int) (*_selection.begin())->note()->velocity());
2637 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 10);
2643 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2645 if (_selection.empty()) {
2662 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2664 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2668 if ((int8_t) (*i)->note()->note() + delta > 127) {
2675 start_note_diff_command (_("transpose"));
2677 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2678 Selection::iterator next = i;
2680 change_note_note (*i, delta, true);
2688 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2694 /* grab the current grid distance */
2696 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2698 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2699 cerr << "Grid type not available as beats - TO BE FIXED\n";
2709 start_note_diff_command (_("change note lengths"));
2711 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2712 Selection::iterator next = i;
2715 /* note the negation of the delta for start */
2717 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2726 MidiRegionView::nudge_notes (bool forward)
2728 if (_selection.empty()) {
2732 /* pick a note as the point along the timeline to get the nudge distance.
2733 its not necessarily the earliest note, so we may want to pull the notes out
2734 into a vector and sort before using the first one.
2737 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2739 framepos_t distance;
2741 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2743 /* grid is off - use nudge distance */
2745 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2751 framepos_t next_pos = ref_point;
2754 if (max_framepos - 1 < next_pos) {
2758 if (next_pos == 0) {
2764 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2765 distance = ref_point - next_pos;
2768 if (distance == 0) {
2772 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2778 start_note_diff_command (_("nudge"));
2780 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2781 Selection::iterator next = i;
2783 change_note_time (*i, delta, true);
2791 MidiRegionView::change_channel(uint8_t channel)
2793 start_note_diff_command(_("change channel"));
2794 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2795 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2803 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2805 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2807 pre_enter_cursor = editor->get_canvas_cursor ();
2809 if (_mouse_state == SelectTouchDragging) {
2810 note_selected (ev, true);
2813 show_verbose_canvas_cursor (ev->note ());
2817 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2819 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2821 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2822 (*i)->hide_velocity ();
2825 editor->hide_verbose_canvas_cursor ();
2827 if (pre_enter_cursor) {
2828 editor->set_canvas_cursor (pre_enter_cursor);
2829 pre_enter_cursor = 0;
2834 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
2837 s << ((int) ev->patch()->program() + 1) << ":" << (ev->patch()->bank() + 1);
2838 trackview.editor().show_verbose_canvas_cursor_with (s.str().c_str(), 10, 20);
2842 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
2844 trackview.editor().hide_verbose_canvas_cursor ();
2848 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
2850 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2852 if (x_fraction > 0.0 && x_fraction < 0.25) {
2853 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
2854 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
2855 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
2857 if (pre_enter_cursor && can_set_cursor) {
2858 editor->set_canvas_cursor (pre_enter_cursor);
2864 MidiRegionView::set_frame_color()
2868 TimeAxisViewItem::set_frame_color ();
2875 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2876 } else if (high_enough_for_name) {
2877 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2882 if (!rect_visible) {
2883 f = UINT_RGBA_CHANGE_A (f, 0);
2886 frame->property_fill_color_rgba() = f;
2890 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2894 case FilterChannels:
2895 _force_channel = -1;
2898 _force_channel = mask;
2899 mask = 0xFFFF; // Show all notes as active (below)
2902 // Update notes for selection
2903 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2904 (*i)->on_channel_selection_change(mask);
2907 _last_channel_selection = mask;
2909 _patch_changes.clear ();
2910 display_patch_changes ();
2914 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2916 _model_name = model;
2917 _custom_device_mode = custom_device_mode;
2922 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2924 if (_selection.empty()) {
2928 PublicEditor& editor (trackview.editor());
2933 editor.get_cut_buffer().add (selection_as_cut_buffer());
2941 start_note_diff_command();
2943 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2949 note_diff_remove_note (*i);
2959 MidiRegionView::selection_as_cut_buffer () const
2963 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2964 NoteType* n = (*i)->note().get();
2965 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2968 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2974 /** This method handles undo */
2976 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
2982 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
2984 trackview.session()->begin_reversible_command (_("paste"));
2986 start_note_diff_command (_("paste"));
2988 Evoral::MusicalTime beat_delta;
2989 Evoral::MusicalTime paste_pos_beats;
2990 Evoral::MusicalTime duration;
2991 Evoral::MusicalTime end_point = 0;
2993 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2994 paste_pos_beats = frames_to_beats (pos - _region->position());
2995 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2996 paste_pos_beats = 0;
2998 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",
2999 (*mcb.notes().begin())->time(),
3000 (*mcb.notes().rbegin())->end_time(),
3001 duration, pos, _region->position(),
3002 paste_pos_beats, beat_delta));
3006 for (int n = 0; n < (int) times; ++n) {
3008 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3010 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3011 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3013 /* make all newly added notes selected */
3015 note_diff_add_note (copied_note, true);
3016 end_point = copied_note->end_time();
3019 paste_pos_beats += duration;
3022 /* if we pasted past the current end of the region, extend the region */
3024 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
3025 framepos_t region_end = _region->position() + _region->length() - 1;
3027 if (end_frame > region_end) {
3029 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3031 _region->clear_changes ();
3032 _region->set_length (end_frame, this);
3033 trackview.session()->add_command (new StatefulDiffCommand (_region));
3038 trackview.session()->commit_reversible_command ();
3041 struct EventNoteTimeEarlyFirstComparator {
3042 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3043 return a->note()->time() < b->note()->time();
3048 MidiRegionView::time_sort_events ()
3050 if (!_sort_needed) {
3054 EventNoteTimeEarlyFirstComparator cmp;
3057 _sort_needed = false;
3061 MidiRegionView::goto_next_note ()
3063 // framepos_t pos = -1;
3064 bool use_next = false;
3066 if (_events.back()->selected()) {
3070 time_sort_events ();
3072 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3073 if ((*i)->selected()) {
3076 } else if (use_next) {
3078 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3083 /* use the first one */
3085 unique_select (_events.front());
3090 MidiRegionView::goto_previous_note ()
3092 // framepos_t pos = -1;
3093 bool use_next = false;
3095 if (_events.front()->selected()) {
3099 time_sort_events ();
3101 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3102 if ((*i)->selected()) {
3105 } else if (use_next) {
3107 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3112 /* use the last one */
3114 unique_select (*(_events.rbegin()));
3118 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3120 bool had_selected = false;
3122 time_sort_events ();
3124 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3125 if ((*i)->selected()) {
3126 selected.insert ((*i)->note());
3127 had_selected = true;
3131 if (allow_all_if_none_selected && !had_selected) {
3132 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3133 selected.insert ((*i)->note());
3139 MidiRegionView::update_ghost_note (double x, double y)
3144 _note_group->w2i (x, y);
3145 framepos_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
3146 trackview.editor().snap_to (f);
3147 f -= _region->position ();
3150 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
3156 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
3158 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
3159 _ghost_note->note()->set_length (length);
3160 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3162 /* the ghost note does not appear in ghost regions, so pass false in here */
3163 update_note (_ghost_note, false);
3165 show_verbose_canvas_cursor (_ghost_note->note ());
3169 MidiRegionView::create_ghost_note (double x, double y)
3174 boost::shared_ptr<NoteType> g (new NoteType);
3175 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3176 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3177 update_ghost_note (x, y);
3178 _ghost_note->show ();
3183 show_verbose_canvas_cursor (_ghost_note->note ());
3187 MidiRegionView::snap_changed ()
3193 create_ghost_note (_last_ghost_x, _last_ghost_y);
3197 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
3200 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
3201 Evoral::midi_note_name (n->note()).c_str(),
3203 (int) n->velocity());
3204 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 20);
3208 MidiRegionView::drop_down_keys ()
3210 _mouse_state = None;
3214 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3216 double note = midi_stream_view()->y_to_note(y);
3218 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3220 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3222 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3223 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3224 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3225 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3230 bool add_mrv_selection = false;
3232 if (_selection.empty()) {
3233 add_mrv_selection = true;
3236 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3237 if (_selection.insert (*i).second) {
3238 (*i)->set_selected (true);
3242 if (add_mrv_selection) {
3243 PublicEditor& editor (trackview.editor());
3244 editor.get_selection().add (this);
3249 MidiRegionView::color_handler ()
3251 RegionView::color_handler ();
3253 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3254 (*i)->set_selected ((*i)->selected()); // will change color
3257 /* XXX probably more to do here */
3261 MidiRegionView::enable_display (bool yn)
3263 RegionView::enable_display (yn);
3270 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3272 if (_step_edit_cursor == 0) {
3273 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3275 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3276 _step_edit_cursor->property_y1() = 0;
3277 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3278 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3279 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3282 move_step_edit_cursor (pos);
3283 _step_edit_cursor->show ();
3287 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3289 _step_edit_cursor_position = pos;
3291 if (_step_edit_cursor) {
3292 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3293 _step_edit_cursor->property_x1() = pixel;
3294 set_step_edit_cursor_width (_step_edit_cursor_width);
3299 MidiRegionView::hide_step_edit_cursor ()
3301 if (_step_edit_cursor) {
3302 _step_edit_cursor->hide ();
3307 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3309 _step_edit_cursor_width = beats;
3311 if (_step_edit_cursor) {
3312 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
3316 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3317 * @param buf Data that has been recorded.
3318 * @param w Source that this data will end up in.
3321 MidiRegionView::data_recorded (boost::shared_ptr<MidiBuffer> buf, boost::weak_ptr<MidiSource> w)
3323 if (!_active_notes) {
3324 /* we aren't actively being recorded to */
3328 boost::shared_ptr<MidiSource> src = w.lock ();
3329 if (!src || src != midi_region()->midi_source()) {
3330 /* recorded data was not destined for our source */
3334 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3335 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3337 framepos_t back = max_framepos;
3339 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3340 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3341 assert (ev.buffer ());
3343 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3345 if (ev.type() == MIDI_CMD_NOTE_ON) {
3347 boost::shared_ptr<NoteType> note (
3348 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3351 add_note (note, true);
3353 /* fix up our note range */
3354 if (ev.note() < _current_range_min) {
3355 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3356 } else if (ev.note() > _current_range_max) {
3357 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3360 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3361 resolve_note (ev.note (), time_beats);
3367 midi_stream_view()->check_record_layers (region(), back);
3371 MidiRegionView::trim_front_starting ()
3373 /* Reparent the note group to the region view's parent, so that it doesn't change
3374 when the region view is trimmed.
3376 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3377 _temporary_note_group->move (group->property_x(), group->property_y());
3378 _note_group->reparent (*_temporary_note_group);
3382 MidiRegionView::trim_front_ending ()
3384 _note_group->reparent (*group);
3385 delete _temporary_note_group;
3386 _temporary_note_group = 0;
3388 if (_region->start() < 0) {
3389 /* Trim drag made start time -ve; fix this */
3390 midi_region()->fix_negative_start ();
3394 /** @return channel (counted from 0) to add an event to, based on the current setting
3395 * of the channel selector.
3398 MidiRegionView::get_channel_for_add () const
3400 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3401 uint16_t const chn_mask = mtv->channel_selector().get_selected_channels();
3403 uint8_t channel = 0;
3405 /* pick the highest selected channel, unless all channels are selected,
3406 which is interpreted to mean channel 1 (zero)
3409 for (uint16_t i = 0; i < 16; ++i) {
3410 if (chn_mask & (1<<i)) {
3416 if (chn_cnt == 16) {
3424 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3426 PatchChangeDialog d (&_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3427 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3431 change_patch_change (pc->patch(), d.patch ());