2 Copyright (C) 2001-2007 Paul Davis
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) {
1014 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
1018 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1019 (*i)->invalidate ();
1022 MidiModel::ReadLock lock(_model->read_lock());
1024 MidiModel::Notes& notes (_model->notes());
1025 _optimization_iterator = _events.begin();
1027 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1029 boost::shared_ptr<NoteType> note (*n);
1030 CanvasNoteEvent* cne;
1033 if (note_in_region_range (note, visible)) {
1035 if ((cne = find_canvas_note (note)) != 0) {
1042 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1044 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1056 add_note (note, visible);
1061 if ((cne = find_canvas_note (note)) != 0) {
1069 /* remove note items that are no longer valid */
1071 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1072 if (!(*i)->valid ()) {
1074 i = _events.erase (i);
1080 _patch_changes.clear();
1084 display_patch_changes ();
1086 _marked_for_selection.clear ();
1087 _marked_for_velocity.clear ();
1089 /* we may have caused _events to contain things out of order (e.g. if a note
1090 moved earlier or later). we don't generally need them in time order, but
1091 make a note that a sort is required for those cases that require it.
1094 _sort_needed = true;
1098 MidiRegionView::display_patch_changes ()
1100 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1101 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1103 for (uint8_t i = 0; i < 16; ++i) {
1104 if (chn_mask & (1<<i)) {
1105 display_patch_changes_on_channel (i);
1111 MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
1113 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1115 if ((*i)->channel() != channel) {
1119 MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
1121 boost::shared_ptr<MIDI::Name::Patch> patch =
1122 MIDI::Name::MidiPatchManager::instance().find_patch(
1123 _model_name, _custom_device_mode, channel, patch_key);
1126 add_canvas_patch_change (*i, patch->name());
1129 /* program and bank numbers are zero-based: convert to one-based */
1130 snprintf (buf, 16, "%d\n%d", (*i)->program() + 1, (*i)->bank() + 1);
1131 add_canvas_patch_change (*i, buf);
1137 MidiRegionView::display_sysexes()
1139 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1140 Evoral::MusicalTime time = (*i)->time();
1145 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1146 str << int((*i)->buffer()[b]);
1147 if (b != (*i)->size() -1) {
1151 string text = str.str();
1153 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1155 double height = midi_stream_view()->contents_height();
1157 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1158 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1160 // Show unless patch change is beyond the region bounds
1161 if (time - _region->start() >= _region->length() || time < _region->start()) {
1167 _sys_exes.push_back(sysex);
1172 MidiRegionView::~MidiRegionView ()
1174 in_destructor = true;
1176 trackview.editor().hide_verbose_canvas_cursor ();
1178 note_delete_connection.disconnect ();
1180 delete _list_editor;
1182 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1184 if (_active_notes) {
1192 delete _note_diff_command;
1193 delete _step_edit_cursor;
1194 delete _temporary_note_group;
1198 MidiRegionView::region_resized (const PropertyChange& what_changed)
1200 RegionView::region_resized(what_changed);
1202 if (what_changed.contains (ARDOUR::Properties::position)) {
1203 set_duration(_region->length(), 0);
1204 if (_enable_display) {
1211 MidiRegionView::reset_width_dependent_items (double pixel_width)
1213 RegionView::reset_width_dependent_items(pixel_width);
1214 assert(_pixel_width == pixel_width);
1216 if (_enable_display) {
1220 move_step_edit_cursor (_step_edit_cursor_position);
1221 set_step_edit_cursor_width (_step_edit_cursor_width);
1225 MidiRegionView::set_height (double height)
1227 static const double FUDGE = 2.0;
1228 const double old_height = _height;
1229 RegionView::set_height(height);
1230 _height = height - FUDGE;
1232 apply_note_range(midi_stream_view()->lowest_note(),
1233 midi_stream_view()->highest_note(),
1234 height != old_height + FUDGE);
1237 name_pixbuf->raise_to_top();
1240 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1241 (*x)->set_height (midi_stream_view()->contents_height());
1244 if (_step_edit_cursor) {
1245 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1250 /** Apply the current note range from the stream view
1251 * by repositioning/hiding notes as necessary
1254 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1256 if (!_enable_display) {
1260 if (!force && _current_range_min == min && _current_range_max == max) {
1264 _current_range_min = min;
1265 _current_range_max = max;
1267 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1268 CanvasNoteEvent* event = *i;
1269 boost::shared_ptr<NoteType> note (event->note());
1271 if (note->note() < _current_range_min ||
1272 note->note() > _current_range_max) {
1278 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1280 const double y1 = midi_stream_view()->note_to_y(note->note());
1281 const double y2 = y1 + floor(midi_stream_view()->note_height());
1283 cnote->property_y1() = y1;
1284 cnote->property_y2() = y2;
1286 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1288 const double diamond_size = update_hit (chit);
1290 chit->set_height (diamond_size);
1296 MidiRegionView::add_ghost (TimeAxisView& tv)
1300 double unit_position = _region->position () / samples_per_unit;
1301 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1302 MidiGhostRegion* ghost;
1304 if (mtv && mtv->midi_view()) {
1305 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1306 to allow having midi notes on top of note lines and waveforms.
1308 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1310 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1313 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1314 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1315 ghost->add_note(note);
1319 ghost->set_height ();
1320 ghost->set_duration (_region->length() / samples_per_unit);
1321 ghosts.push_back (ghost);
1323 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1329 /** Begin tracking note state for successive calls to add_event
1332 MidiRegionView::begin_write()
1334 assert(!_active_notes);
1335 _active_notes = new CanvasNote*[128];
1336 for (unsigned i=0; i < 128; ++i) {
1337 _active_notes[i] = 0;
1342 /** Destroy note state for add_event
1345 MidiRegionView::end_write()
1347 delete[] _active_notes;
1349 _marked_for_selection.clear();
1350 _marked_for_velocity.clear();
1354 /** Resolve an active MIDI note (while recording).
1357 MidiRegionView::resolve_note(uint8_t note, double end_time)
1359 if (midi_view()->note_mode() != Sustained) {
1363 if (_active_notes && _active_notes[note]) {
1365 const framepos_t end_time_frames = beats_to_frames(end_time);
1367 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1368 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1369 _active_notes[note] = 0;
1374 /** Extend active notes to rightmost edge of region (if length is changed)
1377 MidiRegionView::extend_active_notes()
1379 if (!_active_notes) {
1383 for (unsigned i=0; i < 128; ++i) {
1384 if (_active_notes[i]) {
1385 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1392 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1394 if (no_sound_notes || !trackview.editor().sound_notes()) {
1398 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1400 if (!route_ui || !route_ui->midi_track()) {
1404 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1410 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1412 if (no_sound_notes || !trackview.editor().sound_notes()) {
1416 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1418 if (!route_ui || !route_ui->midi_track()) {
1422 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1424 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1433 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1435 const framepos_t note_start_frames = beats_to_frames(note->time());
1437 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1438 (note_start_frames < _region->start());
1440 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1441 (note->note() <= midi_stream_view()->highest_note());
1446 /** Update a canvas note's size from its model note.
1447 * @param ev Canvas note to update.
1448 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1451 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1453 boost::shared_ptr<NoteType> note = ev->note();
1455 const framepos_t note_start_frames = beats_to_frames(note->time());
1457 /* trim note display to not overlap the end of its region */
1458 const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1460 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1461 const double y1 = midi_stream_view()->note_to_y(note->note());
1462 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1464 ev->property_x1() = x;
1465 ev->property_y1() = y1;
1467 if (note->length() > 0) {
1468 ev->property_x2() = note_endpixel;
1470 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1473 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1475 if (note->length() == 0) {
1476 if (_active_notes) {
1477 assert(note->note() < 128);
1478 // If this note is already active there's a stuck note,
1479 // finish the old note rectangle
1480 if (_active_notes[note->note()]) {
1481 CanvasNote* const old_rect = _active_notes[note->note()];
1482 boost::shared_ptr<NoteType> old_note = old_rect->note();
1483 old_rect->property_x2() = x;
1484 old_rect->property_outline_what() = (guint32) 0xF;
1486 _active_notes[note->note()] = ev;
1488 /* outline all but right edge */
1489 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1491 /* outline all edges */
1492 ev->property_outline_what() = (guint32) 0xF;
1495 if (update_ghost_regions) {
1496 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1497 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1499 gr->update_note (ev);
1506 MidiRegionView::update_hit (CanvasHit* ev)
1508 boost::shared_ptr<NoteType> note = ev->note();
1510 const framepos_t note_start_frames = beats_to_frames(note->time());
1511 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1512 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1513 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1517 return diamond_size;
1520 /** Add a MIDI note to the view (with length).
1522 * If in sustained mode, notes with length 0 will be considered active
1523 * notes, and resolve_note should be called when the corresponding note off
1524 * event arrives, to properly display the note.
1527 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1529 CanvasNoteEvent* event = 0;
1531 assert(note->time() >= 0);
1532 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1534 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1536 if (midi_view()->note_mode() == Sustained) {
1538 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1540 update_note (ev_rect);
1544 MidiGhostRegion* gr;
1546 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1547 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1548 gr->add_note(ev_rect);
1552 } else if (midi_view()->note_mode() == Percussive) {
1554 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1556 CanvasHit* ev_diamond = new CanvasHit(*this, *_note_group, diamond_size, note);
1558 update_hit (ev_diamond);
1567 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1568 note_selected(event, true);
1571 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1572 event->show_velocity();
1575 event->on_channel_selection_change(_last_channel_selection);
1576 _events.push_back(event);
1585 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1586 MidiStreamView* const view = mtv->midi_view();
1588 view->update_note_range(note->note());
1592 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1593 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1595 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1597 /* potentially extend region to hold new note */
1599 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1600 framepos_t region_end = _region->position() + _region->length() - 1;
1602 if (end_frame > region_end) {
1603 _region->set_length (end_frame - _region->position(), this);
1606 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1607 MidiStreamView* const view = mtv->midi_view();
1609 view->update_note_range(new_note->note());
1611 _marked_for_selection.clear ();
1614 start_note_diff_command (_("step add"));
1615 note_diff_add_note (new_note, true, false);
1618 // last_step_edit_note = new_note;
1622 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1624 change_note_lengths (false, false, beats, false, true);
1628 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
1630 assert (patch->time() >= 0);
1632 const double x = trackview.editor().frame_to_pixel (beats_to_frames (patch->time()));
1634 double const height = midi_stream_view()->contents_height();
1636 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1637 new CanvasPatchChange(*this, *_note_group,
1642 _custom_device_mode,
1646 // Show unless patch change is beyond the region bounds
1647 if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1648 patch_change->hide();
1650 patch_change->show();
1653 _patch_changes.push_back (patch_change);
1657 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1659 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1660 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1664 if (i != _model->patch_changes().end()) {
1665 key.msb = (*i)->bank_msb ();
1666 key.lsb = (*i)->bank_lsb ();
1667 key.program_number = (*i)->program ();
1669 key.msb = key.lsb = key.program_number = 0;
1672 assert (key.is_sane());
1677 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1679 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1681 if (pc.patch()->program() != new_patch.program_number) {
1682 c->change_program (pc.patch (), new_patch.program_number);
1685 int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1686 if (pc.patch()->bank() != new_bank) {
1687 c->change_bank (pc.patch (), new_bank);
1690 _model->apply_command (*trackview.session(), c);
1692 _patch_changes.clear ();
1693 display_patch_changes ();
1697 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1699 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1701 if (old_change->time() != new_change.time()) {
1702 c->change_time (old_change, new_change.time());
1705 if (old_change->channel() != new_change.channel()) {
1706 c->change_channel (old_change, new_change.channel());
1709 if (old_change->program() != new_change.program()) {
1710 c->change_program (old_change, new_change.program());
1713 if (old_change->bank() != new_change.bank()) {
1714 c->change_bank (old_change, new_change.bank());
1717 _model->apply_command (*trackview.session(), c);
1719 _patch_changes.clear ();
1720 display_patch_changes ();
1723 /** Add a patch change to the region.
1724 * @param t Time in frames relative to region position
1725 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1726 * get_channel_for_add())
1729 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1731 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1732 c->add (MidiModel::PatchChangePtr (
1733 new Evoral::PatchChange<Evoral::MusicalTime> (
1734 frames_to_beats (t + midi_region()->start()), get_channel_for_add(), patch.program(), patch.bank()
1738 _model->apply_command (*trackview.session(), c);
1740 _patch_changes.clear ();
1741 display_patch_changes ();
1745 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1747 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1748 c->change_time (pc.patch (), t);
1749 _model->apply_command (*trackview.session(), c);
1751 _patch_changes.clear ();
1752 display_patch_changes ();
1756 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1758 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1759 c->remove (pc->patch ());
1760 _model->apply_command (*trackview.session(), c);
1762 _patch_changes.clear ();
1763 display_patch_changes ();
1767 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1769 if (patch.patch()->program() < 127) {
1770 MIDI::Name::PatchPrimaryKey key;
1771 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1772 key.program_number++;
1773 change_patch_change (patch, key);
1778 MidiRegionView::next_patch (CanvasPatchChange& patch)
1780 if (patch.patch()->program() > 0) {
1781 MIDI::Name::PatchPrimaryKey key;
1782 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1783 key.program_number--;
1784 change_patch_change (patch, key);
1789 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1791 if (_selection.empty()) {
1795 if (_selection.erase (cne) > 0) {
1796 cerr << "Erased a CNE from selection\n";
1801 MidiRegionView::delete_selection()
1803 if (_selection.empty()) {
1807 start_note_diff_command (_("delete selection"));
1809 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1810 if ((*i)->selected()) {
1811 _note_diff_command->remove((*i)->note());
1821 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1823 start_note_diff_command (_("delete note"));
1824 _note_diff_command->remove (n);
1827 trackview.editor().hide_verbose_canvas_cursor ();
1831 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1833 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1834 if ((*i)->selected() && (*i) != ev) {
1835 (*i)->set_selected(false);
1836 (*i)->hide_velocity();
1844 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1846 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1849 Selection::iterator tmp = i;
1852 (*i)->set_selected (false);
1853 _selection.erase (i);
1862 /* don't bother with removing this regionview from the editor selection,
1863 since we're about to add another note, and thus put/keep this
1864 regionview in the editor selection.
1867 if (!ev->selected()) {
1868 add_to_selection (ev);
1873 MidiRegionView::select_all_notes ()
1877 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1878 add_to_selection (*i);
1883 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1885 uint8_t low_note = 127;
1886 uint8_t high_note = 0;
1887 MidiModel::Notes& notes (_model->notes());
1888 _optimization_iterator = _events.begin();
1894 if (extend && _selection.empty()) {
1900 /* scan existing selection to get note range */
1902 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1903 if ((*i)->note()->note() < low_note) {
1904 low_note = (*i)->note()->note();
1906 if ((*i)->note()->note() > high_note) {
1907 high_note = (*i)->note()->note();
1911 low_note = min (low_note, notenum);
1912 high_note = max (high_note, notenum);
1915 no_sound_notes = true;
1917 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1919 boost::shared_ptr<NoteType> note (*n);
1920 CanvasNoteEvent* cne;
1921 bool select = false;
1923 if (((1 << note->channel()) & channel_mask) != 0) {
1925 if ((note->note() >= low_note && note->note() <= high_note)) {
1928 } else if (note->note() == notenum) {
1934 if ((cne = find_canvas_note (note)) != 0) {
1935 // extend is false because we've taken care of it,
1936 // since it extends by time range, not pitch.
1937 note_selected (cne, add, false);
1941 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1945 no_sound_notes = false;
1949 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1951 MidiModel::Notes& notes (_model->notes());
1952 _optimization_iterator = _events.begin();
1954 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1956 boost::shared_ptr<NoteType> note (*n);
1957 CanvasNoteEvent* cne;
1959 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1960 if ((cne = find_canvas_note (note)) != 0) {
1961 if (cne->selected()) {
1962 note_deselected (cne);
1964 note_selected (cne, true, false);
1972 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1975 clear_selection_except(ev);
1980 if (!ev->selected()) {
1981 add_to_selection (ev);
1985 /* find end of latest note selected, select all between that and the start of "ev" */
1987 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1988 Evoral::MusicalTime latest = 0;
1990 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1991 if ((*i)->note()->end_time() > latest) {
1992 latest = (*i)->note()->end_time();
1994 if ((*i)->note()->time() < earliest) {
1995 earliest = (*i)->note()->time();
1999 if (ev->note()->end_time() > latest) {
2000 latest = ev->note()->end_time();
2003 if (ev->note()->time() < earliest) {
2004 earliest = ev->note()->time();
2007 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2009 /* find notes entirely within OR spanning the earliest..latest range */
2011 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2012 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2013 add_to_selection (*i);
2017 /* if events were guaranteed to be time sorted, we could do this.
2018 but as of sept 10th 2009, they no longer are.
2021 if ((*i)->note()->time() > latest) {
2030 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2032 remove_from_selection (ev);
2036 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
2046 // TODO: Make this faster by storing the last updated selection rect, and only
2047 // adjusting things that are in the area that appears/disappeared.
2048 // We probably need a tree to be able to find events in O(log(n)) time.
2050 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2052 /* check if any corner of the note is inside the rect
2055 1) this is computing "touched by", not "contained by" the rect.
2056 2) this does not require that events be sorted in time.
2059 const double ix1 = (*i)->x1();
2060 const double ix2 = (*i)->x2();
2061 const double iy1 = (*i)->y1();
2062 const double iy2 = (*i)->y2();
2064 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2065 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2066 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2067 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2070 if (!(*i)->selected()) {
2071 add_to_selection (*i);
2073 } else if ((*i)->selected()) {
2074 // Not inside rectangle
2075 remove_from_selection (*i);
2081 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2083 Selection::iterator i = _selection.find (ev);
2085 if (i != _selection.end()) {
2086 _selection.erase (i);
2089 ev->set_selected (false);
2090 ev->hide_velocity ();
2092 if (_selection.empty()) {
2093 PublicEditor& editor (trackview.editor());
2094 editor.get_selection().remove (this);
2099 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2101 bool add_mrv_selection = false;
2103 if (_selection.empty()) {
2104 add_mrv_selection = true;
2107 if (_selection.insert (ev).second) {
2108 ev->set_selected (true);
2109 play_midi_note ((ev)->note());
2112 if (add_mrv_selection) {
2113 PublicEditor& editor (trackview.editor());
2114 editor.get_selection().add (this);
2119 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2121 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2122 PossibleChord to_play;
2123 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2125 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2126 if ((*i)->note()->time() < earliest) {
2127 earliest = (*i)->note()->time();
2131 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2132 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2133 to_play.push_back ((*i)->note());
2135 (*i)->move_event(dx, dy);
2138 if (dy && !_selection.empty() && !no_sound_notes && trackview.editor().sound_notes()) {
2140 if (to_play.size() > 1) {
2142 PossibleChord shifted;
2144 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2145 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2146 moved_note->set_note (moved_note->note() + cumulative_dy);
2147 shifted.push_back (moved_note);
2150 play_midi_chord (shifted);
2152 } else if (!to_play.empty()) {
2154 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2155 moved_note->set_note (moved_note->note() + cumulative_dy);
2156 play_midi_note (moved_note);
2162 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2164 assert (!_selection.empty());
2166 uint8_t lowest_note_in_selection = 127;
2167 uint8_t highest_note_in_selection = 0;
2168 uint8_t highest_note_difference = 0;
2170 // find highest and lowest notes first
2172 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2173 uint8_t pitch = (*i)->note()->note();
2174 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2175 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2179 cerr << "dnote: " << (int) dnote << endl;
2180 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2181 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2182 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2183 << int(highest_note_in_selection) << endl;
2184 cerr << "selection size: " << _selection.size() << endl;
2185 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2188 // Make sure the note pitch does not exceed the MIDI standard range
2189 if (highest_note_in_selection + dnote > 127) {
2190 highest_note_difference = highest_note_in_selection - 127;
2193 start_note_diff_command (_("move notes"));
2195 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2197 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2203 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2205 uint8_t original_pitch = (*i)->note()->note();
2206 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2208 // keep notes in standard midi range
2209 clamp_to_0_127(new_pitch);
2211 // keep original pitch if note is dragged outside valid midi range
2212 if ((original_pitch != 0 && new_pitch == 0)
2213 || (original_pitch != 127 && new_pitch == 127)) {
2214 new_pitch = original_pitch;
2217 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2218 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2220 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2225 // care about notes being moved beyond the upper/lower bounds on the canvas
2226 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2227 highest_note_in_selection > midi_stream_view()->highest_note()) {
2228 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2233 MidiRegionView::snap_pixel_to_frame(double x)
2235 PublicEditor& editor = trackview.editor();
2236 // x is region relative, convert it to global absolute frames
2237 framepos_t frame = editor.pixel_to_frame(x) + _region->position();
2238 editor.snap_to(frame);
2239 return frame - _region->position(); // convert back to region relative
2243 MidiRegionView::snap_frame_to_frame(framepos_t x)
2245 PublicEditor& editor = trackview.editor();
2246 // x is region relative, convert it to global absolute frames
2247 framepos_t frame = x + _region->position();
2248 editor.snap_to(frame);
2249 return frame - _region->position(); // convert back to region relative
2253 MidiRegionView::snap_to_pixel(double x)
2255 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2259 MidiRegionView::get_position_pixels()
2261 framepos_t region_frame = get_position();
2262 return trackview.editor().frame_to_pixel(region_frame);
2266 MidiRegionView::get_end_position_pixels()
2268 framepos_t frame = get_position() + get_duration ();
2269 return trackview.editor().frame_to_pixel(frame);
2273 MidiRegionView::beats_to_frames(double beats) const
2275 return _time_converter.to(beats);
2279 MidiRegionView::frames_to_beats(framepos_t frames) const
2281 return _time_converter.from(frames);
2285 MidiRegionView::begin_resizing (bool /*at_front*/)
2287 _resize_data.clear();
2289 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2290 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2292 // only insert CanvasNotes into the map
2294 NoteResizeData *resize_data = new NoteResizeData();
2295 resize_data->canvas_note = note;
2297 // create a new SimpleRect from the note which will be the resize preview
2298 SimpleRect *resize_rect = new SimpleRect(
2299 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2301 // calculate the colors: get the color settings
2302 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2303 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2306 // make the resize preview notes more transparent and bright
2307 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2309 // calculate color based on note velocity
2310 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2311 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2315 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2316 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2318 resize_data->resize_rect = resize_rect;
2319 _resize_data.push_back(resize_data);
2324 /** Update resizing notes while user drags.
2325 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2326 * @param at_front which end of the note (true == note on, false == note off)
2327 * @param delta_x change in mouse position since the start of the drag
2328 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2329 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2330 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2331 * as the \a primary note.
2334 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2336 bool cursor_set = false;
2338 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2339 SimpleRect* resize_rect = (*i)->resize_rect;
2340 CanvasNote* canvas_note = (*i)->canvas_note;
2345 current_x = canvas_note->x1() + delta_x;
2347 current_x = primary->x1() + delta_x;
2351 current_x = canvas_note->x2() + delta_x;
2353 current_x = primary->x2() + delta_x;
2358 resize_rect->property_x1() = snap_to_pixel(current_x);
2359 resize_rect->property_x2() = canvas_note->x2();
2361 resize_rect->property_x2() = snap_to_pixel(current_x);
2362 resize_rect->property_x1() = canvas_note->x1();
2368 beats = snap_pixel_to_frame (current_x);
2369 beats = frames_to_beats (beats);
2374 if (beats < canvas_note->note()->end_time()) {
2375 len = canvas_note->note()->time() - beats;
2376 len += canvas_note->note()->length();
2381 if (beats >= canvas_note->note()->time()) {
2382 len = beats - canvas_note->note()->time();
2389 snprintf (buf, sizeof (buf), "%.3g beats", len);
2390 trackview.editor().show_verbose_canvas_cursor_with (buf);
2399 /** Finish resizing notes when the user releases the mouse button.
2400 * Parameters the same as for \a update_resizing().
2403 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2405 start_note_diff_command (_("resize notes"));
2407 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2408 CanvasNote* canvas_note = (*i)->canvas_note;
2409 SimpleRect* resize_rect = (*i)->resize_rect;
2414 current_x = canvas_note->x1() + delta_x;
2416 current_x = primary->x1() + delta_x;
2420 current_x = canvas_note->x2() + delta_x;
2422 current_x = primary->x2() + delta_x;
2426 current_x = snap_pixel_to_frame (current_x);
2427 current_x = frames_to_beats (current_x);
2429 if (at_front && current_x < canvas_note->note()->end_time()) {
2430 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2432 double len = canvas_note->note()->time() - current_x;
2433 len += canvas_note->note()->length();
2436 /* XXX convert to beats */
2437 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2442 double len = current_x - canvas_note->note()->time();
2445 /* XXX convert to beats */
2446 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2454 _resize_data.clear();
2459 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2461 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, (uint8_t) channel);
2465 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2467 uint8_t new_velocity;
2470 new_velocity = event->note()->velocity() + velocity;
2471 clamp_to_0_127(new_velocity);
2473 new_velocity = velocity;
2476 event->set_selected (event->selected()); // change color
2478 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2482 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2487 new_note = event->note()->note() + note;
2492 clamp_to_0_127 (new_note);
2493 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2497 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2499 bool change_start = false;
2500 bool change_length = false;
2501 Evoral::MusicalTime new_start = 0;
2502 Evoral::MusicalTime new_length = 0;
2504 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2506 front_delta: if positive - move the start of the note later in time (shortening it)
2507 if negative - move the start of the note earlier in time (lengthening it)
2509 end_delta: if positive - move the end of the note later in time (lengthening it)
2510 if negative - move the end of the note earlier in time (shortening it)
2514 if (front_delta < 0) {
2516 if (event->note()->time() < -front_delta) {
2519 new_start = event->note()->time() + front_delta; // moves earlier
2522 /* start moved toward zero, so move the end point out to where it used to be.
2523 Note that front_delta is negative, so this increases the length.
2526 new_length = event->note()->length() - front_delta;
2527 change_start = true;
2528 change_length = true;
2532 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2534 if (new_pos < event->note()->end_time()) {
2535 new_start = event->note()->time() + front_delta;
2536 /* start moved toward the end, so move the end point back to where it used to be */
2537 new_length = event->note()->length() - front_delta;
2538 change_start = true;
2539 change_length = true;
2546 bool can_change = true;
2547 if (end_delta < 0) {
2548 if (event->note()->length() < -end_delta) {
2554 new_length = event->note()->length() + end_delta;
2555 change_length = true;
2560 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2563 if (change_length) {
2564 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2569 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2571 Evoral::MusicalTime new_time;
2575 if (event->note()->time() < -delta) {
2578 new_time = event->note()->time() + delta;
2581 new_time = event->note()->time() + delta;
2587 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2591 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2593 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2597 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2601 if (_selection.empty()) {
2616 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2617 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2623 start_note_diff_command (_("change velocities"));
2625 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2626 Selection::iterator next = i;
2628 change_note_velocity (*i, delta, true);
2634 if (!_selection.empty()) {
2636 snprintf (buf, sizeof (buf), "Vel %d",
2637 (int) (*_selection.begin())->note()->velocity());
2638 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 10);
2644 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2646 if (_selection.empty()) {
2663 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2665 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2669 if ((int8_t) (*i)->note()->note() + delta > 127) {
2676 start_note_diff_command (_("transpose"));
2678 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2679 Selection::iterator next = i;
2681 change_note_note (*i, delta, true);
2689 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2695 /* grab the current grid distance */
2697 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2699 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2700 cerr << "Grid type not available as beats - TO BE FIXED\n";
2710 start_note_diff_command (_("change note lengths"));
2712 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2713 Selection::iterator next = i;
2716 /* note the negation of the delta for start */
2718 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2727 MidiRegionView::nudge_notes (bool forward)
2729 if (_selection.empty()) {
2733 /* pick a note as the point along the timeline to get the nudge distance.
2734 its not necessarily the earliest note, so we may want to pull the notes out
2735 into a vector and sort before using the first one.
2738 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2740 framepos_t distance;
2742 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2744 /* grid is off - use nudge distance */
2746 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2752 framepos_t next_pos = ref_point;
2755 if (max_framepos - 1 < next_pos) {
2759 if (next_pos == 0) {
2765 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2766 distance = ref_point - next_pos;
2769 if (distance == 0) {
2773 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2779 start_note_diff_command (_("nudge"));
2781 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2782 Selection::iterator next = i;
2784 change_note_time (*i, delta, true);
2792 MidiRegionView::change_channel(uint8_t channel)
2794 start_note_diff_command(_("change channel"));
2795 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2796 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2804 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2806 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2808 pre_enter_cursor = editor->get_canvas_cursor ();
2810 if (_mouse_state == SelectTouchDragging) {
2811 note_selected (ev, true);
2814 show_verbose_canvas_cursor (ev->note ());
2818 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2820 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2822 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2823 (*i)->hide_velocity ();
2826 editor->hide_verbose_canvas_cursor ();
2828 if (pre_enter_cursor) {
2829 editor->set_canvas_cursor (pre_enter_cursor);
2830 pre_enter_cursor = 0;
2835 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
2838 s << ((int) ev->patch()->program() + 1) << ":" << (ev->patch()->bank() + 1);
2839 trackview.editor().show_verbose_canvas_cursor_with (s.str().c_str(), 10, 20);
2843 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
2845 trackview.editor().hide_verbose_canvas_cursor ();
2849 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
2851 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2853 if (x_fraction > 0.0 && x_fraction < 0.25) {
2854 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
2855 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
2856 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
2858 if (pre_enter_cursor && can_set_cursor) {
2859 editor->set_canvas_cursor (pre_enter_cursor);
2865 MidiRegionView::set_frame_color()
2869 TimeAxisViewItem::set_frame_color ();
2876 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2877 } else if (high_enough_for_name) {
2878 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2883 if (!rect_visible) {
2884 f = UINT_RGBA_CHANGE_A (f, 0);
2887 frame->property_fill_color_rgba() = f;
2891 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2895 case FilterChannels:
2896 _force_channel = -1;
2899 _force_channel = mask;
2900 mask = 0xFFFF; // Show all notes as active (below)
2903 // Update notes for selection
2904 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2905 (*i)->on_channel_selection_change(mask);
2908 _last_channel_selection = mask;
2910 _patch_changes.clear ();
2911 display_patch_changes ();
2915 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2917 _model_name = model;
2918 _custom_device_mode = custom_device_mode;
2923 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2925 if (_selection.empty()) {
2929 PublicEditor& editor (trackview.editor());
2934 editor.get_cut_buffer().add (selection_as_cut_buffer());
2942 start_note_diff_command();
2944 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2950 note_diff_remove_note (*i);
2960 MidiRegionView::selection_as_cut_buffer () const
2964 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2965 NoteType* n = (*i)->note().get();
2966 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2969 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2975 /** This method handles undo */
2977 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
2983 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
2985 trackview.session()->begin_reversible_command (_("paste"));
2987 start_note_diff_command (_("paste"));
2989 Evoral::MusicalTime beat_delta;
2990 Evoral::MusicalTime paste_pos_beats;
2991 Evoral::MusicalTime duration;
2992 Evoral::MusicalTime end_point = 0;
2994 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2995 paste_pos_beats = frames_to_beats (pos - _region->position());
2996 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2997 paste_pos_beats = 0;
2999 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",
3000 (*mcb.notes().begin())->time(),
3001 (*mcb.notes().rbegin())->end_time(),
3002 duration, pos, _region->position(),
3003 paste_pos_beats, beat_delta));
3007 for (int n = 0; n < (int) times; ++n) {
3009 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3011 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3012 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3014 /* make all newly added notes selected */
3016 note_diff_add_note (copied_note, true);
3017 end_point = copied_note->end_time();
3020 paste_pos_beats += duration;
3023 /* if we pasted past the current end of the region, extend the region */
3025 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
3026 framepos_t region_end = _region->position() + _region->length() - 1;
3028 if (end_frame > region_end) {
3030 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3032 _region->clear_changes ();
3033 _region->set_length (end_frame, this);
3034 trackview.session()->add_command (new StatefulDiffCommand (_region));
3039 trackview.session()->commit_reversible_command ();
3042 struct EventNoteTimeEarlyFirstComparator {
3043 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3044 return a->note()->time() < b->note()->time();
3049 MidiRegionView::time_sort_events ()
3051 if (!_sort_needed) {
3055 EventNoteTimeEarlyFirstComparator cmp;
3058 _sort_needed = false;
3062 MidiRegionView::goto_next_note ()
3064 // framepos_t pos = -1;
3065 bool use_next = false;
3067 if (_events.back()->selected()) {
3071 time_sort_events ();
3073 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3074 if ((*i)->selected()) {
3077 } else if (use_next) {
3079 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3084 /* use the first one */
3086 unique_select (_events.front());
3091 MidiRegionView::goto_previous_note ()
3093 // framepos_t pos = -1;
3094 bool use_next = false;
3096 if (_events.front()->selected()) {
3100 time_sort_events ();
3102 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3103 if ((*i)->selected()) {
3106 } else if (use_next) {
3108 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3113 /* use the last one */
3115 unique_select (*(_events.rbegin()));
3119 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3121 bool had_selected = false;
3123 time_sort_events ();
3125 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3126 if ((*i)->selected()) {
3127 selected.insert ((*i)->note());
3128 had_selected = true;
3132 if (allow_all_if_none_selected && !had_selected) {
3133 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3134 selected.insert ((*i)->note());
3140 MidiRegionView::update_ghost_note (double x, double y)
3145 _note_group->w2i (x, y);
3146 framepos_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
3147 trackview.editor().snap_to (f);
3148 f -= _region->position ();
3151 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
3157 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
3159 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
3160 _ghost_note->note()->set_length (length);
3161 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3163 /* the ghost note does not appear in ghost regions, so pass false in here */
3164 update_note (_ghost_note, false);
3166 show_verbose_canvas_cursor (_ghost_note->note ());
3170 MidiRegionView::create_ghost_note (double x, double y)
3175 boost::shared_ptr<NoteType> g (new NoteType);
3176 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3177 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3178 update_ghost_note (x, y);
3179 _ghost_note->show ();
3184 show_verbose_canvas_cursor (_ghost_note->note ());
3188 MidiRegionView::snap_changed ()
3194 create_ghost_note (_last_ghost_x, _last_ghost_y);
3198 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
3201 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
3202 Evoral::midi_note_name (n->note()).c_str(),
3204 (int) n->velocity());
3205 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 20);
3209 MidiRegionView::drop_down_keys ()
3211 _mouse_state = None;
3215 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3217 double note = midi_stream_view()->y_to_note(y);
3219 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3221 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3223 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3224 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3225 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3226 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3231 bool add_mrv_selection = false;
3233 if (_selection.empty()) {
3234 add_mrv_selection = true;
3237 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3238 if (_selection.insert (*i).second) {
3239 (*i)->set_selected (true);
3243 if (add_mrv_selection) {
3244 PublicEditor& editor (trackview.editor());
3245 editor.get_selection().add (this);
3250 MidiRegionView::color_handler ()
3252 RegionView::color_handler ();
3254 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3255 (*i)->set_selected ((*i)->selected()); // will change color
3258 /* XXX probably more to do here */
3262 MidiRegionView::enable_display (bool yn)
3264 RegionView::enable_display (yn);
3271 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3273 if (_step_edit_cursor == 0) {
3274 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3276 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3277 _step_edit_cursor->property_y1() = 0;
3278 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3279 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3280 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3283 move_step_edit_cursor (pos);
3284 _step_edit_cursor->show ();
3288 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3290 _step_edit_cursor_position = pos;
3292 if (_step_edit_cursor) {
3293 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3294 _step_edit_cursor->property_x1() = pixel;
3295 set_step_edit_cursor_width (_step_edit_cursor_width);
3300 MidiRegionView::hide_step_edit_cursor ()
3302 if (_step_edit_cursor) {
3303 _step_edit_cursor->hide ();
3308 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3310 _step_edit_cursor_width = beats;
3312 if (_step_edit_cursor) {
3313 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
3317 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3318 * @param buf Data that has been recorded.
3319 * @param w Source that this data will end up in.
3322 MidiRegionView::data_recorded (boost::shared_ptr<MidiBuffer> buf, boost::weak_ptr<MidiSource> w)
3324 if (!_active_notes) {
3325 /* we aren't actively being recorded to */
3329 boost::shared_ptr<MidiSource> src = w.lock ();
3330 if (!src || src != midi_region()->midi_source()) {
3331 /* recorded data was not destined for our source */
3335 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3336 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3338 framepos_t back = max_framepos;
3340 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3341 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3342 assert (ev.buffer ());
3344 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3346 if (ev.type() == MIDI_CMD_NOTE_ON) {
3348 boost::shared_ptr<NoteType> note (
3349 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3352 add_note (note, true);
3354 /* fix up our note range */
3355 if (ev.note() < _current_range_min) {
3356 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3357 } else if (ev.note() > _current_range_max) {
3358 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3361 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3362 resolve_note (ev.note (), time_beats);
3368 midi_stream_view()->check_record_layers (region(), back);
3372 MidiRegionView::trim_front_starting ()
3374 /* Reparent the note group to the region view's parent, so that it doesn't change
3375 when the region view is trimmed.
3377 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3378 _temporary_note_group->move (group->property_x(), group->property_y());
3379 _note_group->reparent (*_temporary_note_group);
3383 MidiRegionView::trim_front_ending ()
3385 _note_group->reparent (*group);
3386 delete _temporary_note_group;
3387 _temporary_note_group = 0;
3389 if (_region->start() < 0) {
3390 /* Trim drag made start time -ve; fix this */
3391 midi_region()->fix_negative_start ();
3395 /** @return channel (counted from 0) to add an event to, based on the current setting
3396 * of the channel selector.
3399 MidiRegionView::get_channel_for_add () const
3401 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3402 uint16_t const chn_mask = mtv->channel_selector().get_selected_channels();
3404 uint8_t channel = 0;
3406 /* pick the highest selected channel, unless all channels are selected,
3407 which is interpreted to mean channel 1 (zero)
3410 for (uint16_t i = 0; i < 16; ++i) {
3411 if (chn_mask & (1<<i)) {
3417 if (chn_cnt == 16) {
3425 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3427 PatchChangeDialog d (&_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3428 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3432 change_patch_change (pc->patch(), d.patch ());