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)
383 group->w2i (_last_x, _last_y);
385 if (_mouse_state != SelectTouchDragging) {
387 _pressed_button = ev->button;
388 _mouse_state = Pressed;
393 _pressed_button = ev->button;
399 MidiRegionView::button_release (GdkEventButton* ev)
401 double event_x, event_y;
402 framepos_t event_frame = 0;
407 group->w2i(event_x, event_y);
408 group->ungrab(ev->time);
410 event_frame = trackview.editor().pixel_to_frame(event_x);
412 switch (_mouse_state) {
413 case Pressed: // Clicked
415 switch (trackview.editor().current_mouse_mode()) {
421 if (Keyboard::is_insert_note_event(ev)){
423 double event_x, event_y;
427 group->w2i(event_x, event_y);
430 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
436 create_note_at (event_x, event_y, beats, true);
444 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
450 create_note_at (event_x, event_y, beats, true);
461 case SelectRectDragging: // Select drag done
468 case AddDragging: // Add drag done
472 if (Keyboard::is_insert_note_event(ev) || trackview.editor().current_mouse_mode() == MouseRange){
474 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
476 const double x = _drag_rect->property_x1();
477 const double length = trackview.editor().pixel_to_frame (_drag_rect->property_x2() - _drag_rect->property_x1());
479 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), true);
486 create_ghost_note (ev->x, ev->y);
496 MidiRegionView::motion (GdkEventMotion* ev)
498 double event_x, event_y;
499 framepos_t event_frame = 0;
503 group->w2i(event_x, event_y);
505 // convert event_x to global frame
506 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
507 trackview.editor().snap_to(event_frame);
509 // convert event_frame back to local coordinates relative to position
510 event_frame -= _region->position();
512 if (!_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
513 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())
514 && _mouse_state != AddDragging){
516 create_ghost_note (ev->x, ev->y);
518 else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
519 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())){
521 update_ghost_note (ev->x, ev->y);
523 else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange){
528 trackview.editor().hide_verbose_canvas_cursor ();
530 else if (_ghost_note && trackview.editor().current_mouse_mode() == MouseRange) {
531 update_ghost_note (ev->x, ev->y);
534 /* any motion immediately hides velocity text that may have been visible */
536 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
537 (*i)->hide_velocity ();
540 switch (_mouse_state) {
541 case Pressed: // Maybe start a drag, if we've moved a bit
543 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
544 /* no appreciable movement since the button was pressed */
549 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject
550 && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
552 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
553 Gdk::Cursor(Gdk::FLEUR), ev->time);
557 _drag_start_x = event_x;
558 _drag_start_y = event_y;
560 _drag_rect = new ArdourCanvas::SimpleRect(*group);
561 _drag_rect->property_x1() = event_x;
562 _drag_rect->property_y1() = event_y;
563 _drag_rect->property_x2() = event_x;
564 _drag_rect->property_y2() = event_y;
565 _drag_rect->property_outline_what() = 0xFF;
566 _drag_rect->property_outline_color_rgba()
567 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
568 _drag_rect->property_fill_color_rgba()
569 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
571 _mouse_state = SelectRectDragging;
574 // Add note drag start
575 } else if (trackview.editor().internal_editing()) {
580 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
581 Gdk::Cursor(Gdk::FLEUR), ev->time);
585 _drag_start_x = event_x;
586 _drag_start_y = event_y;
588 _drag_rect = new ArdourCanvas::SimpleRect(*group);
589 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
591 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
592 midi_stream_view()->y_to_note(event_y));
593 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
594 _drag_rect->property_y2() = _drag_rect->property_y1()
595 + floor(midi_stream_view()->note_height());
596 _drag_rect->property_outline_what() = 0xFF;
597 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
598 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
600 _mouse_state = AddDragging;
607 trackview.editor().hide_verbose_canvas_cursor ();
615 case SelectRectDragging: // Select drag motion
616 case AddDragging: // Add note drag motion
621 GdkModifierType state;
622 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
627 if (_mouse_state == AddDragging){
628 event_x = trackview.editor().frame_to_pixel(event_frame);
633 if (event_x > _drag_start_x){
634 _drag_rect->property_x2() = event_x;
637 _drag_rect->property_x1() = event_x;
641 if (_drag_rect && _mouse_state == SelectRectDragging) {
643 if (event_y > _drag_start_y){
644 _drag_rect->property_y2() = event_y;
647 _drag_rect->property_y1() = event_y;
650 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
656 case SelectTouchDragging:
668 MidiRegionView::scroll (GdkEventScroll* ev)
670 if (_selection.empty()) {
674 trackview.editor().hide_verbose_canvas_cursor ();
676 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
678 if (ev->direction == GDK_SCROLL_UP) {
679 change_velocities (true, fine, false);
680 } else if (ev->direction == GDK_SCROLL_DOWN) {
681 change_velocities (false, fine, false);
687 MidiRegionView::key_press (GdkEventKey* ev)
689 /* since GTK bindings are generally activated on press, and since
690 detectable auto-repeat is the name of the game and only sends
691 repeated presses, carry out key actions at key press, not release.
694 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
695 _mouse_state = SelectTouchDragging;
698 } else if (ev->keyval == GDK_Escape) {
702 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
704 bool start = (ev->keyval == GDK_comma);
705 bool end = (ev->keyval == GDK_period);
706 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
707 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
709 change_note_lengths (fine, shorter, 0.0, start, end);
713 } else if (ev->keyval == GDK_Delete) {
718 } else if (ev->keyval == GDK_Tab) {
720 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
721 goto_previous_note ();
727 } else if (ev->keyval == GDK_Up) {
729 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
730 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
732 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
733 change_velocities (true, fine, allow_smush);
735 transpose (true, fine, allow_smush);
739 } else if (ev->keyval == GDK_Down) {
741 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
742 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
744 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
745 change_velocities (false, fine, allow_smush);
747 transpose (false, fine, allow_smush);
751 } else if (ev->keyval == GDK_Left) {
756 } else if (ev->keyval == GDK_Right) {
761 } else if (ev->keyval == GDK_Control_L) {
770 MidiRegionView::key_release (GdkEventKey* ev)
772 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
780 MidiRegionView::show_list_editor ()
783 _list_editor = new MidiListEditor (trackview.session(), midi_region());
785 _list_editor->present ();
788 /** Add a note to the model, and the view, at a canvas (click) coordinate.
789 * \param x horizontal position in pixels
790 * \param y vertical position in pixels
791 * \param length duration of the note in beats, which will be snapped to the grid
792 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
795 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
797 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
798 MidiStreamView* const view = mtv->midi_view();
800 double note = view->y_to_note(y);
803 assert(note <= 127.0);
805 // Start of note in frames relative to region start
806 framepos_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
807 assert(start_frames >= 0);
810 length = frames_to_beats(
811 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
813 assert (length != 0);
816 length = frames_to_beats (beats_to_frames (length) - 1);
819 const boost::shared_ptr<NoteType> new_note (new NoteType (get_channel_for_add (),
820 frames_to_beats(start_frames + _region->start()), length,
821 (uint8_t)note, 0x40));
823 if (_model->contains (new_note)) {
827 view->update_note_range(new_note->note());
829 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note");
831 _model->apply_command(*trackview.session(), cmd);
833 play_midi_note (new_note);
837 MidiRegionView::clear_events()
842 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
843 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
848 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
853 _patch_changes.clear();
855 _optimization_iterator = _events.end();
859 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
863 content_connection.disconnect ();
864 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
868 if (_enable_display) {
874 MidiRegionView::start_note_diff_command (string name)
876 if (!_note_diff_command) {
877 _note_diff_command = _model->new_note_diff_command (name);
882 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
884 if (_note_diff_command) {
885 _note_diff_command->add (note);
888 _marked_for_selection.insert(note);
891 _marked_for_velocity.insert(note);
896 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
898 if (_note_diff_command && ev->note()) {
899 _note_diff_command->remove(ev->note());
904 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
905 MidiModel::NoteDiffCommand::Property property,
908 if (_note_diff_command) {
909 _note_diff_command->change (ev->note(), property, val);
914 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
915 MidiModel::NoteDiffCommand::Property property,
916 Evoral::MusicalTime val)
918 if (_note_diff_command) {
919 _note_diff_command->change (ev->note(), property, val);
924 MidiRegionView::apply_diff (bool as_subcommand)
928 if (!_note_diff_command) {
932 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
933 // Mark all selected notes for selection when model reloads
934 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
935 _marked_for_selection.insert((*i)->note());
940 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
942 _model->apply_command (*trackview.session(), _note_diff_command);
945 _note_diff_command = 0;
946 midi_view()->midi_track()->playlist_modified();
949 _marked_for_selection.clear();
952 _marked_for_velocity.clear();
956 MidiRegionView::abort_command()
958 delete _note_diff_command;
959 _note_diff_command = 0;
964 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
966 if (_optimization_iterator != _events.end()) {
967 ++_optimization_iterator;
970 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
971 return *_optimization_iterator;
974 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
975 if ((*_optimization_iterator)->note() == note) {
976 return *_optimization_iterator;
984 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
986 MidiModel::Notes notes;
987 _model->get_notes (notes, op, val, chan_mask);
989 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
990 CanvasNoteEvent* cne = find_canvas_note (*n);
998 MidiRegionView::redisplay_model()
1000 // Don't redisplay the model if we're currently recording and displaying that
1001 if (_active_notes) {
1006 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
1010 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1011 (*i)->invalidate ();
1014 MidiModel::ReadLock lock(_model->read_lock());
1016 MidiModel::Notes& notes (_model->notes());
1017 _optimization_iterator = _events.begin();
1019 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1021 boost::shared_ptr<NoteType> note (*n);
1022 CanvasNoteEvent* cne;
1025 if (note_in_region_range (note, visible)) {
1027 if ((cne = find_canvas_note (note)) != 0) {
1034 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1036 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1048 add_note (note, visible);
1053 if ((cne = find_canvas_note (note)) != 0) {
1061 /* remove note items that are no longer valid */
1063 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1064 if (!(*i)->valid ()) {
1066 i = _events.erase (i);
1072 _patch_changes.clear();
1076 display_patch_changes ();
1078 _marked_for_selection.clear ();
1079 _marked_for_velocity.clear ();
1081 /* we may have caused _events to contain things out of order (e.g. if a note
1082 moved earlier or later). we don't generally need them in time order, but
1083 make a note that a sort is required for those cases that require it.
1086 _sort_needed = true;
1090 MidiRegionView::display_patch_changes ()
1092 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1093 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1095 for (uint8_t i = 0; i < 16; ++i) {
1096 if (chn_mask & (1<<i)) {
1097 display_patch_changes_on_channel (i);
1103 MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
1105 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1107 if ((*i)->channel() != channel) {
1111 MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
1113 boost::shared_ptr<MIDI::Name::Patch> patch =
1114 MIDI::Name::MidiPatchManager::instance().find_patch(
1115 _model_name, _custom_device_mode, channel, patch_key);
1118 add_canvas_patch_change (*i, patch->name());
1121 /* program and bank numbers are zero-based: convert to one-based */
1122 snprintf (buf, 16, "%d\n%d", (*i)->program() + 1, (*i)->bank() + 1);
1123 add_canvas_patch_change (*i, buf);
1129 MidiRegionView::display_sysexes()
1131 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1132 Evoral::MusicalTime time = (*i)->time();
1137 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1138 str << int((*i)->buffer()[b]);
1139 if (b != (*i)->size() -1) {
1143 string text = str.str();
1145 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1147 double height = midi_stream_view()->contents_height();
1149 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1150 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1152 // Show unless patch change is beyond the region bounds
1153 if (time - _region->start() >= _region->length() || time < _region->start()) {
1159 _sys_exes.push_back(sysex);
1164 MidiRegionView::~MidiRegionView ()
1166 in_destructor = true;
1168 trackview.editor().hide_verbose_canvas_cursor ();
1170 note_delete_connection.disconnect ();
1172 delete _list_editor;
1174 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1176 if (_active_notes) {
1184 delete _note_diff_command;
1185 delete _step_edit_cursor;
1186 delete _temporary_note_group;
1190 MidiRegionView::region_resized (const PropertyChange& what_changed)
1192 RegionView::region_resized(what_changed);
1194 if (what_changed.contains (ARDOUR::Properties::position)) {
1195 set_duration(_region->length(), 0);
1196 if (_enable_display) {
1203 MidiRegionView::reset_width_dependent_items (double pixel_width)
1205 RegionView::reset_width_dependent_items(pixel_width);
1206 assert(_pixel_width == pixel_width);
1208 if (_enable_display) {
1212 move_step_edit_cursor (_step_edit_cursor_position);
1213 set_step_edit_cursor_width (_step_edit_cursor_width);
1217 MidiRegionView::set_height (double height)
1219 static const double FUDGE = 2.0;
1220 const double old_height = _height;
1221 RegionView::set_height(height);
1222 _height = height - FUDGE;
1224 apply_note_range(midi_stream_view()->lowest_note(),
1225 midi_stream_view()->highest_note(),
1226 height != old_height + FUDGE);
1229 name_pixbuf->raise_to_top();
1232 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1233 (*x)->set_height (midi_stream_view()->contents_height());
1236 if (_step_edit_cursor) {
1237 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1242 /** Apply the current note range from the stream view
1243 * by repositioning/hiding notes as necessary
1246 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1248 if (!_enable_display) {
1252 if (!force && _current_range_min == min && _current_range_max == max) {
1256 _current_range_min = min;
1257 _current_range_max = max;
1259 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1260 CanvasNoteEvent* event = *i;
1261 boost::shared_ptr<NoteType> note (event->note());
1263 if (note->note() < _current_range_min ||
1264 note->note() > _current_range_max) {
1270 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1272 const double y1 = midi_stream_view()->note_to_y(note->note());
1273 const double y2 = y1 + floor(midi_stream_view()->note_height());
1275 cnote->property_y1() = y1;
1276 cnote->property_y2() = y2;
1278 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1280 const double diamond_size = update_hit (chit);
1282 chit->set_height (diamond_size);
1288 MidiRegionView::add_ghost (TimeAxisView& tv)
1292 double unit_position = _region->position () / samples_per_unit;
1293 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1294 MidiGhostRegion* ghost;
1296 if (mtv && mtv->midi_view()) {
1297 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1298 to allow having midi notes on top of note lines and waveforms.
1300 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1302 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1305 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1306 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1307 ghost->add_note(note);
1311 ghost->set_height ();
1312 ghost->set_duration (_region->length() / samples_per_unit);
1313 ghosts.push_back (ghost);
1315 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1321 /** Begin tracking note state for successive calls to add_event
1324 MidiRegionView::begin_write()
1326 assert(!_active_notes);
1327 _active_notes = new CanvasNote*[128];
1328 for (unsigned i=0; i < 128; ++i) {
1329 _active_notes[i] = 0;
1334 /** Destroy note state for add_event
1337 MidiRegionView::end_write()
1339 delete[] _active_notes;
1341 _marked_for_selection.clear();
1342 _marked_for_velocity.clear();
1346 /** Resolve an active MIDI note (while recording).
1349 MidiRegionView::resolve_note(uint8_t note, double end_time)
1351 if (midi_view()->note_mode() != Sustained) {
1355 if (_active_notes && _active_notes[note]) {
1357 const framepos_t end_time_frames = beats_to_frames(end_time);
1359 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1360 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1361 _active_notes[note] = 0;
1366 /** Extend active notes to rightmost edge of region (if length is changed)
1369 MidiRegionView::extend_active_notes()
1371 if (!_active_notes) {
1375 for (unsigned i=0; i < 128; ++i) {
1376 if (_active_notes[i]) {
1377 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1384 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1386 if (no_sound_notes || !trackview.editor().sound_notes()) {
1390 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1392 if (!route_ui || !route_ui->midi_track()) {
1396 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1402 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1404 if (no_sound_notes || !trackview.editor().sound_notes()) {
1408 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1410 if (!route_ui || !route_ui->midi_track()) {
1414 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1416 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1425 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1427 const framepos_t note_start_frames = beats_to_frames(note->time());
1429 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1430 (note_start_frames < _region->start());
1432 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1433 (note->note() <= midi_stream_view()->highest_note());
1438 /** Update a canvas note's size from its model note.
1439 * @param ev Canvas note to update.
1440 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1443 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1445 boost::shared_ptr<NoteType> note = ev->note();
1447 const framepos_t note_start_frames = beats_to_frames(note->time());
1449 /* trim note display to not overlap the end of its region */
1450 const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1452 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1453 const double y1 = midi_stream_view()->note_to_y(note->note());
1454 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1456 ev->property_x1() = x;
1457 ev->property_y1() = y1;
1459 if (note->length() > 0) {
1460 ev->property_x2() = note_endpixel;
1462 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1465 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1467 if (note->length() == 0) {
1468 if (_active_notes) {
1469 assert(note->note() < 128);
1470 // If this note is already active there's a stuck note,
1471 // finish the old note rectangle
1472 if (_active_notes[note->note()]) {
1473 CanvasNote* const old_rect = _active_notes[note->note()];
1474 boost::shared_ptr<NoteType> old_note = old_rect->note();
1475 old_rect->property_x2() = x;
1476 old_rect->property_outline_what() = (guint32) 0xF;
1478 _active_notes[note->note()] = ev;
1480 /* outline all but right edge */
1481 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1483 /* outline all edges */
1484 ev->property_outline_what() = (guint32) 0xF;
1487 if (update_ghost_regions) {
1488 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1489 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1491 gr->update_note (ev);
1498 MidiRegionView::update_hit (CanvasHit* ev)
1500 boost::shared_ptr<NoteType> note = ev->note();
1502 const framepos_t note_start_frames = beats_to_frames(note->time());
1503 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1504 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1505 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1509 return diamond_size;
1512 /** Add a MIDI note to the view (with length).
1514 * If in sustained mode, notes with length 0 will be considered active
1515 * notes, and resolve_note should be called when the corresponding note off
1516 * event arrives, to properly display the note.
1519 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1521 CanvasNoteEvent* event = 0;
1523 assert(note->time() >= 0);
1524 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1526 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1528 if (midi_view()->note_mode() == Sustained) {
1530 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1532 update_note (ev_rect);
1536 MidiGhostRegion* gr;
1538 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1539 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1540 gr->add_note(ev_rect);
1544 } else if (midi_view()->note_mode() == Percussive) {
1546 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1548 CanvasHit* ev_diamond = new CanvasHit(*this, *_note_group, diamond_size, note);
1550 update_hit (ev_diamond);
1559 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1560 note_selected(event, true);
1563 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1564 event->show_velocity();
1567 event->on_channel_selection_change(_last_channel_selection);
1568 _events.push_back(event);
1577 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1578 MidiStreamView* const view = mtv->midi_view();
1580 view->update_note_range(note->note());
1584 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1585 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1587 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1589 /* potentially extend region to hold new note */
1591 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1592 framepos_t region_end = _region->position() + _region->length() - 1;
1594 if (end_frame > region_end) {
1595 _region->set_length (end_frame - _region->position(), this);
1598 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1599 MidiStreamView* const view = mtv->midi_view();
1601 view->update_note_range(new_note->note());
1603 _marked_for_selection.clear ();
1606 start_note_diff_command (_("step add"));
1607 note_diff_add_note (new_note, true, false);
1610 // last_step_edit_note = new_note;
1614 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1616 change_note_lengths (false, false, beats, false, true);
1620 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
1622 assert (patch->time() >= 0);
1624 const double x = trackview.editor().frame_to_pixel (beats_to_frames (patch->time()));
1626 double const height = midi_stream_view()->contents_height();
1628 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1629 new CanvasPatchChange(*this, *_note_group,
1634 _custom_device_mode,
1638 // Show unless patch change is beyond the region bounds
1639 if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1640 patch_change->hide();
1642 patch_change->show();
1645 _patch_changes.push_back (patch_change);
1649 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1651 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1652 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1656 if (i != _model->patch_changes().end()) {
1657 key.msb = (*i)->bank_msb ();
1658 key.lsb = (*i)->bank_lsb ();
1659 key.program_number = (*i)->program ();
1661 key.msb = key.lsb = key.program_number = 0;
1664 assert (key.is_sane());
1669 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1671 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1673 if (pc.patch()->program() != new_patch.program_number) {
1674 c->change_program (pc.patch (), new_patch.program_number);
1677 int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1678 if (pc.patch()->bank() != new_bank) {
1679 c->change_bank (pc.patch (), new_bank);
1682 _model->apply_command (*trackview.session(), c);
1684 _patch_changes.clear ();
1685 display_patch_changes ();
1689 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1691 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1693 if (old_change->time() != new_change.time()) {
1694 c->change_time (old_change, new_change.time());
1697 if (old_change->channel() != new_change.channel()) {
1698 c->change_channel (old_change, new_change.channel());
1701 if (old_change->program() != new_change.program()) {
1702 c->change_program (old_change, new_change.program());
1705 if (old_change->bank() != new_change.bank()) {
1706 c->change_bank (old_change, new_change.bank());
1709 _model->apply_command (*trackview.session(), c);
1711 _patch_changes.clear ();
1712 display_patch_changes ();
1715 /** Add a patch change to the region.
1716 * @param t Time in frames relative to region position
1717 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1718 * get_channel_for_add())
1721 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1723 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1724 c->add (MidiModel::PatchChangePtr (
1725 new Evoral::PatchChange<Evoral::MusicalTime> (
1726 frames_to_beats (t + midi_region()->start()), get_channel_for_add(), patch.program(), patch.bank()
1730 _model->apply_command (*trackview.session(), c);
1732 _patch_changes.clear ();
1733 display_patch_changes ();
1737 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1739 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1740 c->change_time (pc.patch (), t);
1741 _model->apply_command (*trackview.session(), c);
1743 _patch_changes.clear ();
1744 display_patch_changes ();
1748 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1750 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1751 c->remove (pc->patch ());
1752 _model->apply_command (*trackview.session(), c);
1754 _patch_changes.clear ();
1755 display_patch_changes ();
1759 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1761 if (patch.patch()->program() < 127) {
1762 MIDI::Name::PatchPrimaryKey key;
1763 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1764 key.program_number++;
1765 change_patch_change (patch, key);
1770 MidiRegionView::next_patch (CanvasPatchChange& patch)
1772 if (patch.patch()->program() > 0) {
1773 MIDI::Name::PatchPrimaryKey key;
1774 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1775 key.program_number--;
1776 change_patch_change (patch, key);
1781 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1783 if (_selection.empty()) {
1787 if (_selection.erase (cne) > 0) {
1788 cerr << "Erased a CNE from selection\n";
1793 MidiRegionView::delete_selection()
1795 if (_selection.empty()) {
1799 start_note_diff_command (_("delete selection"));
1801 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1802 if ((*i)->selected()) {
1803 _note_diff_command->remove((*i)->note());
1813 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1815 start_note_diff_command (_("delete note"));
1816 _note_diff_command->remove (n);
1819 trackview.editor().hide_verbose_canvas_cursor ();
1823 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1825 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1826 if ((*i)->selected() && (*i) != ev) {
1827 (*i)->set_selected(false);
1828 (*i)->hide_velocity();
1836 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1838 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1841 Selection::iterator tmp = i;
1844 (*i)->set_selected (false);
1845 _selection.erase (i);
1854 /* don't bother with removing this regionview from the editor selection,
1855 since we're about to add another note, and thus put/keep this
1856 regionview in the editor selection.
1859 if (!ev->selected()) {
1860 add_to_selection (ev);
1865 MidiRegionView::select_all_notes ()
1869 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1870 add_to_selection (*i);
1875 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1877 uint8_t low_note = 127;
1878 uint8_t high_note = 0;
1879 MidiModel::Notes& notes (_model->notes());
1880 _optimization_iterator = _events.begin();
1886 if (extend && _selection.empty()) {
1892 /* scan existing selection to get note range */
1894 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1895 if ((*i)->note()->note() < low_note) {
1896 low_note = (*i)->note()->note();
1898 if ((*i)->note()->note() > high_note) {
1899 high_note = (*i)->note()->note();
1903 low_note = min (low_note, notenum);
1904 high_note = max (high_note, notenum);
1907 no_sound_notes = true;
1909 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1911 boost::shared_ptr<NoteType> note (*n);
1912 CanvasNoteEvent* cne;
1913 bool select = false;
1915 if (((1 << note->channel()) & channel_mask) != 0) {
1917 if ((note->note() >= low_note && note->note() <= high_note)) {
1920 } else if (note->note() == notenum) {
1926 if ((cne = find_canvas_note (note)) != 0) {
1927 // extend is false because we've taken care of it,
1928 // since it extends by time range, not pitch.
1929 note_selected (cne, add, false);
1933 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1937 no_sound_notes = false;
1941 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1943 MidiModel::Notes& notes (_model->notes());
1944 _optimization_iterator = _events.begin();
1946 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1948 boost::shared_ptr<NoteType> note (*n);
1949 CanvasNoteEvent* cne;
1951 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1952 if ((cne = find_canvas_note (note)) != 0) {
1953 if (cne->selected()) {
1954 note_deselected (cne);
1956 note_selected (cne, true, false);
1964 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1967 clear_selection_except(ev);
1972 if (!ev->selected()) {
1973 add_to_selection (ev);
1977 /* find end of latest note selected, select all between that and the start of "ev" */
1979 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1980 Evoral::MusicalTime latest = 0;
1982 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1983 if ((*i)->note()->end_time() > latest) {
1984 latest = (*i)->note()->end_time();
1986 if ((*i)->note()->time() < earliest) {
1987 earliest = (*i)->note()->time();
1991 if (ev->note()->end_time() > latest) {
1992 latest = ev->note()->end_time();
1995 if (ev->note()->time() < earliest) {
1996 earliest = ev->note()->time();
1999 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2001 /* find notes entirely within OR spanning the earliest..latest range */
2003 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2004 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2005 add_to_selection (*i);
2009 /* if events were guaranteed to be time sorted, we could do this.
2010 but as of sept 10th 2009, they no longer are.
2013 if ((*i)->note()->time() > latest) {
2022 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2024 remove_from_selection (ev);
2028 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
2038 // TODO: Make this faster by storing the last updated selection rect, and only
2039 // adjusting things that are in the area that appears/disappeared.
2040 // We probably need a tree to be able to find events in O(log(n)) time.
2042 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2044 /* check if any corner of the note is inside the rect
2047 1) this is computing "touched by", not "contained by" the rect.
2048 2) this does not require that events be sorted in time.
2051 const double ix1 = (*i)->x1();
2052 const double ix2 = (*i)->x2();
2053 const double iy1 = (*i)->y1();
2054 const double iy2 = (*i)->y2();
2056 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2057 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2058 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2059 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2062 if (!(*i)->selected()) {
2063 add_to_selection (*i);
2065 } else if ((*i)->selected()) {
2066 // Not inside rectangle
2067 remove_from_selection (*i);
2073 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2075 Selection::iterator i = _selection.find (ev);
2077 if (i != _selection.end()) {
2078 _selection.erase (i);
2081 ev->set_selected (false);
2082 ev->hide_velocity ();
2084 if (_selection.empty()) {
2085 PublicEditor& editor (trackview.editor());
2086 editor.get_selection().remove (this);
2091 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2093 bool add_mrv_selection = false;
2095 if (_selection.empty()) {
2096 add_mrv_selection = true;
2099 if (_selection.insert (ev).second) {
2100 ev->set_selected (true);
2101 play_midi_note ((ev)->note());
2104 if (add_mrv_selection) {
2105 PublicEditor& editor (trackview.editor());
2106 editor.get_selection().add (this);
2111 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2113 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2114 PossibleChord to_play;
2115 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2117 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2118 if ((*i)->note()->time() < earliest) {
2119 earliest = (*i)->note()->time();
2123 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2124 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2125 to_play.push_back ((*i)->note());
2127 (*i)->move_event(dx, dy);
2130 if (dy && !_selection.empty() && !no_sound_notes && trackview.editor().sound_notes()) {
2132 if (to_play.size() > 1) {
2134 PossibleChord shifted;
2136 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2137 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2138 moved_note->set_note (moved_note->note() + cumulative_dy);
2139 shifted.push_back (moved_note);
2142 play_midi_chord (shifted);
2144 } else if (!to_play.empty()) {
2146 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2147 moved_note->set_note (moved_note->note() + cumulative_dy);
2148 play_midi_note (moved_note);
2154 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2156 assert (!_selection.empty());
2158 uint8_t lowest_note_in_selection = 127;
2159 uint8_t highest_note_in_selection = 0;
2160 uint8_t highest_note_difference = 0;
2162 // find highest and lowest notes first
2164 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2165 uint8_t pitch = (*i)->note()->note();
2166 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2167 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2171 cerr << "dnote: " << (int) dnote << endl;
2172 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2173 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2174 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2175 << int(highest_note_in_selection) << endl;
2176 cerr << "selection size: " << _selection.size() << endl;
2177 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2180 // Make sure the note pitch does not exceed the MIDI standard range
2181 if (highest_note_in_selection + dnote > 127) {
2182 highest_note_difference = highest_note_in_selection - 127;
2185 start_note_diff_command (_("move notes"));
2187 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2189 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2195 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2197 uint8_t original_pitch = (*i)->note()->note();
2198 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2200 // keep notes in standard midi range
2201 clamp_to_0_127(new_pitch);
2203 // keep original pitch if note is dragged outside valid midi range
2204 if ((original_pitch != 0 && new_pitch == 0)
2205 || (original_pitch != 127 && new_pitch == 127)) {
2206 new_pitch = original_pitch;
2209 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2210 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2212 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2217 // care about notes being moved beyond the upper/lower bounds on the canvas
2218 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2219 highest_note_in_selection > midi_stream_view()->highest_note()) {
2220 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2225 MidiRegionView::snap_pixel_to_frame(double x)
2227 PublicEditor& editor = trackview.editor();
2228 // x is region relative, convert it to global absolute frames
2229 framepos_t frame = editor.pixel_to_frame(x) + _region->position();
2230 editor.snap_to(frame);
2231 return frame - _region->position(); // convert back to region relative
2235 MidiRegionView::snap_frame_to_frame(framepos_t x)
2237 PublicEditor& editor = trackview.editor();
2238 // x is region relative, convert it to global absolute frames
2239 framepos_t frame = x + _region->position();
2240 editor.snap_to(frame);
2241 return frame - _region->position(); // convert back to region relative
2245 MidiRegionView::snap_to_pixel(double x)
2247 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2251 MidiRegionView::get_position_pixels()
2253 framepos_t region_frame = get_position();
2254 return trackview.editor().frame_to_pixel(region_frame);
2258 MidiRegionView::get_end_position_pixels()
2260 framepos_t frame = get_position() + get_duration ();
2261 return trackview.editor().frame_to_pixel(frame);
2265 MidiRegionView::beats_to_frames(double beats) const
2267 return _time_converter.to(beats);
2271 MidiRegionView::frames_to_beats(framepos_t frames) const
2273 return _time_converter.from(frames);
2277 MidiRegionView::begin_resizing (bool /*at_front*/)
2279 _resize_data.clear();
2281 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2282 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2284 // only insert CanvasNotes into the map
2286 NoteResizeData *resize_data = new NoteResizeData();
2287 resize_data->canvas_note = note;
2289 // create a new SimpleRect from the note which will be the resize preview
2290 SimpleRect *resize_rect = new SimpleRect(
2291 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2293 // calculate the colors: get the color settings
2294 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2295 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2298 // make the resize preview notes more transparent and bright
2299 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2301 // calculate color based on note velocity
2302 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2303 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2307 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2308 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2310 resize_data->resize_rect = resize_rect;
2311 _resize_data.push_back(resize_data);
2316 /** Update resizing notes while user drags.
2317 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2318 * @param at_front which end of the note (true == note on, false == note off)
2319 * @param delta_x change in mouse position since the start of the drag
2320 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2321 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2322 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2323 * as the \a primary note.
2326 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2328 bool cursor_set = false;
2330 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2331 SimpleRect* resize_rect = (*i)->resize_rect;
2332 CanvasNote* canvas_note = (*i)->canvas_note;
2337 current_x = canvas_note->x1() + delta_x;
2339 current_x = primary->x1() + delta_x;
2343 current_x = canvas_note->x2() + delta_x;
2345 current_x = primary->x2() + delta_x;
2350 resize_rect->property_x1() = snap_to_pixel(current_x);
2351 resize_rect->property_x2() = canvas_note->x2();
2353 resize_rect->property_x2() = snap_to_pixel(current_x);
2354 resize_rect->property_x1() = canvas_note->x1();
2360 beats = snap_pixel_to_frame (current_x);
2361 beats = frames_to_beats (beats);
2366 if (beats < canvas_note->note()->end_time()) {
2367 len = canvas_note->note()->time() - beats;
2368 len += canvas_note->note()->length();
2373 if (beats >= canvas_note->note()->time()) {
2374 len = beats - canvas_note->note()->time();
2381 snprintf (buf, sizeof (buf), "%.3g beats", len);
2382 trackview.editor().show_verbose_canvas_cursor_with (buf);
2391 /** Finish resizing notes when the user releases the mouse button.
2392 * Parameters the same as for \a update_resizing().
2395 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2397 start_note_diff_command (_("resize notes"));
2399 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2400 CanvasNote* canvas_note = (*i)->canvas_note;
2401 SimpleRect* resize_rect = (*i)->resize_rect;
2406 current_x = canvas_note->x1() + delta_x;
2408 current_x = primary->x1() + delta_x;
2412 current_x = canvas_note->x2() + delta_x;
2414 current_x = primary->x2() + delta_x;
2418 current_x = snap_pixel_to_frame (current_x);
2419 current_x = frames_to_beats (current_x);
2421 if (at_front && current_x < canvas_note->note()->end_time()) {
2422 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2424 double len = canvas_note->note()->time() - current_x;
2425 len += canvas_note->note()->length();
2428 /* XXX convert to beats */
2429 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2434 double len = current_x - canvas_note->note()->time();
2437 /* XXX convert to beats */
2438 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2446 _resize_data.clear();
2451 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2453 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, (uint8_t) channel);
2457 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2459 uint8_t new_velocity;
2462 new_velocity = event->note()->velocity() + velocity;
2463 clamp_to_0_127(new_velocity);
2465 new_velocity = velocity;
2468 event->set_selected (event->selected()); // change color
2470 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2474 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2479 new_note = event->note()->note() + note;
2484 clamp_to_0_127 (new_note);
2485 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2489 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2491 bool change_start = false;
2492 bool change_length = false;
2493 Evoral::MusicalTime new_start = 0;
2494 Evoral::MusicalTime new_length = 0;
2496 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2498 front_delta: if positive - move the start of the note later in time (shortening it)
2499 if negative - move the start of the note earlier in time (lengthening it)
2501 end_delta: if positive - move the end of the note later in time (lengthening it)
2502 if negative - move the end of the note earlier in time (shortening it)
2506 if (front_delta < 0) {
2508 if (event->note()->time() < -front_delta) {
2511 new_start = event->note()->time() + front_delta; // moves earlier
2514 /* start moved toward zero, so move the end point out to where it used to be.
2515 Note that front_delta is negative, so this increases the length.
2518 new_length = event->note()->length() - front_delta;
2519 change_start = true;
2520 change_length = true;
2524 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2526 if (new_pos < event->note()->end_time()) {
2527 new_start = event->note()->time() + front_delta;
2528 /* start moved toward the end, so move the end point back to where it used to be */
2529 new_length = event->note()->length() - front_delta;
2530 change_start = true;
2531 change_length = true;
2538 bool can_change = true;
2539 if (end_delta < 0) {
2540 if (event->note()->length() < -end_delta) {
2546 new_length = event->note()->length() + end_delta;
2547 change_length = true;
2552 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2555 if (change_length) {
2556 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2561 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2563 Evoral::MusicalTime new_time;
2567 if (event->note()->time() < -delta) {
2570 new_time = event->note()->time() + delta;
2573 new_time = event->note()->time() + delta;
2579 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2583 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2585 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2589 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2593 if (_selection.empty()) {
2608 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2609 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2615 start_note_diff_command (_("change velocities"));
2617 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2618 Selection::iterator next = i;
2620 change_note_velocity (*i, delta, true);
2626 if (!_selection.empty()) {
2628 snprintf (buf, sizeof (buf), "Vel %d",
2629 (int) (*_selection.begin())->note()->velocity());
2630 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 10);
2636 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2638 if (_selection.empty()) {
2655 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2657 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2661 if ((int8_t) (*i)->note()->note() + delta > 127) {
2668 start_note_diff_command (_("transpose"));
2670 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2671 Selection::iterator next = i;
2673 change_note_note (*i, delta, true);
2681 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2687 /* grab the current grid distance */
2689 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2691 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2692 cerr << "Grid type not available as beats - TO BE FIXED\n";
2702 start_note_diff_command (_("change note lengths"));
2704 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2705 Selection::iterator next = i;
2708 /* note the negation of the delta for start */
2710 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2719 MidiRegionView::nudge_notes (bool forward)
2721 if (_selection.empty()) {
2725 /* pick a note as the point along the timeline to get the nudge distance.
2726 its not necessarily the earliest note, so we may want to pull the notes out
2727 into a vector and sort before using the first one.
2730 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2732 framepos_t distance;
2734 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2736 /* grid is off - use nudge distance */
2738 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2744 framepos_t next_pos = ref_point;
2747 if (max_framepos - 1 < next_pos) {
2751 if (next_pos == 0) {
2757 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2758 distance = ref_point - next_pos;
2761 if (distance == 0) {
2765 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2771 start_note_diff_command (_("nudge"));
2773 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2774 Selection::iterator next = i;
2776 change_note_time (*i, delta, true);
2784 MidiRegionView::change_channel(uint8_t channel)
2786 start_note_diff_command(_("change channel"));
2787 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2788 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2796 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2798 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2800 pre_enter_cursor = editor->get_canvas_cursor ();
2802 if (_mouse_state == SelectTouchDragging) {
2803 note_selected (ev, true);
2806 show_verbose_canvas_cursor (ev->note ());
2810 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2812 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2814 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2815 (*i)->hide_velocity ();
2818 editor->hide_verbose_canvas_cursor ();
2820 if (pre_enter_cursor) {
2821 editor->set_canvas_cursor (pre_enter_cursor);
2822 pre_enter_cursor = 0;
2827 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
2830 s << ((int) ev->patch()->program() + 1) << ":" << (ev->patch()->bank() + 1);
2831 trackview.editor().show_verbose_canvas_cursor_with (s.str().c_str(), 10, 20);
2835 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
2837 trackview.editor().hide_verbose_canvas_cursor ();
2841 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
2843 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2845 if (x_fraction > 0.0 && x_fraction < 0.25) {
2846 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
2847 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
2848 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
2850 if (pre_enter_cursor && can_set_cursor) {
2851 editor->set_canvas_cursor (pre_enter_cursor);
2857 MidiRegionView::set_frame_color()
2864 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2865 } else if (high_enough_for_name) {
2866 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2868 frame->property_fill_color_rgba() = fill_color;
2873 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2877 case FilterChannels:
2878 _force_channel = -1;
2881 _force_channel = mask;
2882 mask = 0xFFFF; // Show all notes as active (below)
2885 // Update notes for selection
2886 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2887 (*i)->on_channel_selection_change(mask);
2890 _last_channel_selection = mask;
2892 _patch_changes.clear ();
2893 display_patch_changes ();
2897 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2899 _model_name = model;
2900 _custom_device_mode = custom_device_mode;
2905 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2907 if (_selection.empty()) {
2911 PublicEditor& editor (trackview.editor());
2916 editor.get_cut_buffer().add (selection_as_cut_buffer());
2924 start_note_diff_command();
2926 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2932 note_diff_remove_note (*i);
2942 MidiRegionView::selection_as_cut_buffer () const
2946 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2947 NoteType* n = (*i)->note().get();
2948 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2951 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2957 /** This method handles undo */
2959 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
2965 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
2967 trackview.session()->begin_reversible_command (_("paste"));
2969 start_note_diff_command (_("paste"));
2971 Evoral::MusicalTime beat_delta;
2972 Evoral::MusicalTime paste_pos_beats;
2973 Evoral::MusicalTime duration;
2974 Evoral::MusicalTime end_point = 0;
2976 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2977 paste_pos_beats = frames_to_beats (pos - _region->position());
2978 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2979 paste_pos_beats = 0;
2981 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",
2982 (*mcb.notes().begin())->time(),
2983 (*mcb.notes().rbegin())->end_time(),
2984 duration, pos, _region->position(),
2985 paste_pos_beats, beat_delta));
2989 for (int n = 0; n < (int) times; ++n) {
2991 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2993 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2994 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2996 /* make all newly added notes selected */
2998 note_diff_add_note (copied_note, true);
2999 end_point = copied_note->end_time();
3002 paste_pos_beats += duration;
3005 /* if we pasted past the current end of the region, extend the region */
3007 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
3008 framepos_t region_end = _region->position() + _region->length() - 1;
3010 if (end_frame > region_end) {
3012 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3014 _region->clear_changes ();
3015 _region->set_length (end_frame, this);
3016 trackview.session()->add_command (new StatefulDiffCommand (_region));
3021 trackview.session()->commit_reversible_command ();
3024 struct EventNoteTimeEarlyFirstComparator {
3025 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3026 return a->note()->time() < b->note()->time();
3031 MidiRegionView::time_sort_events ()
3033 if (!_sort_needed) {
3037 EventNoteTimeEarlyFirstComparator cmp;
3040 _sort_needed = false;
3044 MidiRegionView::goto_next_note ()
3046 // framepos_t pos = -1;
3047 bool use_next = false;
3049 if (_events.back()->selected()) {
3053 time_sort_events ();
3055 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3056 if ((*i)->selected()) {
3059 } else if (use_next) {
3061 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3066 /* use the first one */
3068 unique_select (_events.front());
3073 MidiRegionView::goto_previous_note ()
3075 // framepos_t pos = -1;
3076 bool use_next = false;
3078 if (_events.front()->selected()) {
3082 time_sort_events ();
3084 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3085 if ((*i)->selected()) {
3088 } else if (use_next) {
3090 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3095 /* use the last one */
3097 unique_select (*(_events.rbegin()));
3101 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3103 bool had_selected = false;
3105 time_sort_events ();
3107 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3108 if ((*i)->selected()) {
3109 selected.insert ((*i)->note());
3110 had_selected = true;
3114 if (allow_all_if_none_selected && !had_selected) {
3115 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3116 selected.insert ((*i)->note());
3122 MidiRegionView::update_ghost_note (double x, double y)
3127 _note_group->w2i (x, y);
3128 framepos_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
3129 trackview.editor().snap_to (f);
3130 f -= _region->position ();
3133 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
3139 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
3141 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
3142 _ghost_note->note()->set_length (length);
3143 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3145 /* the ghost note does not appear in ghost regions, so pass false in here */
3146 update_note (_ghost_note, false);
3148 show_verbose_canvas_cursor (_ghost_note->note ());
3152 MidiRegionView::create_ghost_note (double x, double y)
3157 boost::shared_ptr<NoteType> g (new NoteType);
3158 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3159 update_ghost_note (x, y);
3160 _ghost_note->show ();
3165 show_verbose_canvas_cursor (_ghost_note->note ());
3169 MidiRegionView::snap_changed ()
3175 create_ghost_note (_last_ghost_x, _last_ghost_y);
3179 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
3182 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
3183 Evoral::midi_note_name (n->note()).c_str(),
3185 (int) n->velocity());
3186 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 20);
3190 MidiRegionView::drop_down_keys ()
3192 _mouse_state = None;
3196 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3198 double note = midi_stream_view()->y_to_note(y);
3200 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3202 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3204 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3205 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3206 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3207 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3212 bool add_mrv_selection = false;
3214 if (_selection.empty()) {
3215 add_mrv_selection = true;
3218 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3219 if (_selection.insert (*i).second) {
3220 (*i)->set_selected (true);
3224 if (add_mrv_selection) {
3225 PublicEditor& editor (trackview.editor());
3226 editor.get_selection().add (this);
3231 MidiRegionView::color_handler ()
3233 RegionView::color_handler ();
3235 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3236 (*i)->set_selected ((*i)->selected()); // will change color
3239 /* XXX probably more to do here */
3243 MidiRegionView::enable_display (bool yn)
3245 RegionView::enable_display (yn);
3252 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3254 if (_step_edit_cursor == 0) {
3255 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3257 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3258 _step_edit_cursor->property_y1() = 0;
3259 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3260 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3261 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3264 move_step_edit_cursor (pos);
3265 _step_edit_cursor->show ();
3269 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3271 _step_edit_cursor_position = pos;
3273 if (_step_edit_cursor) {
3274 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3275 _step_edit_cursor->property_x1() = pixel;
3276 set_step_edit_cursor_width (_step_edit_cursor_width);
3281 MidiRegionView::hide_step_edit_cursor ()
3283 if (_step_edit_cursor) {
3284 _step_edit_cursor->hide ();
3289 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3291 _step_edit_cursor_width = beats;
3293 if (_step_edit_cursor) {
3294 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
3298 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3299 * @param buf Data that has been recorded.
3300 * @param w Source that this data will end up in.
3303 MidiRegionView::data_recorded (boost::shared_ptr<MidiBuffer> buf, boost::weak_ptr<MidiSource> w)
3305 if (!_active_notes) {
3306 /* we aren't actively being recorded to */
3310 boost::shared_ptr<MidiSource> src = w.lock ();
3311 if (!src || src != midi_region()->midi_source()) {
3312 /* recorded data was not destined for our source */
3316 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3317 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3319 framepos_t back = max_framepos;
3321 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3322 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3323 assert (ev.buffer ());
3325 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3327 if (ev.type() == MIDI_CMD_NOTE_ON) {
3329 boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > note (
3330 new Evoral::Note<Evoral::MusicalTime> (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3333 add_note (note, true);
3335 /* fix up our note range */
3336 if (ev.note() < _current_range_min) {
3337 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3338 } else if (ev.note() > _current_range_max) {
3339 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3342 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3343 resolve_note (ev.note (), time_beats);
3349 midi_stream_view()->check_record_layers (region(), back);
3353 MidiRegionView::trim_front_starting ()
3355 /* Reparent the note group to the region view's parent, so that it doesn't change
3356 when the region view is trimmed.
3358 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3359 _temporary_note_group->move (group->property_x(), group->property_y());
3360 _note_group->reparent (*_temporary_note_group);
3364 MidiRegionView::trim_front_ending ()
3366 _note_group->reparent (*group);
3367 delete _temporary_note_group;
3368 _temporary_note_group = 0;
3370 if (_region->start() < 0) {
3371 /* Trim drag made start time -ve; fix this */
3372 midi_region()->fix_negative_start ();
3376 /** @return channel (counted from 0) to add an event to, based on the current setting
3377 * of the channel selector.
3380 MidiRegionView::get_channel_for_add () const
3382 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3383 uint16_t const chn_mask = mtv->channel_selector().get_selected_channels();
3385 uint8_t channel = 0;
3387 /* pick the highest selected channel, unless all channels are selected,
3388 which is interpreted to mean channel 1 (zero)
3391 for (uint16_t i = 0; i < 16; ++i) {
3392 if (chn_mask & (1<<i)) {
3398 if (chn_cnt == 16) {
3406 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3408 PatchChangeDialog d (&_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3409 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3413 change_patch_change (pc->patch(), d.patch ());