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"
53 #include "ghostregion.h"
54 #include "gui_thread.h"
56 #include "midi_cut_buffer.h"
57 #include "midi_list_editor.h"
58 #include "midi_region_view.h"
59 #include "midi_streamview.h"
60 #include "midi_time_axis.h"
61 #include "midi_util.h"
62 #include "note_player.h"
63 #include "public_editor.h"
64 #include "rgb_macros.h"
65 #include "selection.h"
66 #include "simpleline.h"
67 #include "streamview.h"
69 #include "mouse_cursors.h"
70 #include "patch_change_dialog.h"
74 using namespace ARDOUR;
76 using namespace Editing;
77 using namespace ArdourCanvas;
78 using Gtkmm2ext::Keyboard;
80 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
81 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
82 : RegionView (parent, tv, r, spu, basic_color)
84 , _last_channel_selection(0xFFFF)
85 , _current_range_min(0)
86 , _current_range_max(0)
87 , _model_name(string())
88 , _custom_device_mode(string())
90 , _note_group(new ArdourCanvas::Group(*group))
91 , _note_diff_command (0)
94 , _step_edit_cursor (0)
95 , _step_edit_cursor_width (1.0)
96 , _step_edit_cursor_position (0.0)
97 , _channel_selection_scoped_note (0)
98 , _temporary_note_group (0)
101 , _sort_needed (true)
102 , _optimization_iterator (_events.end())
104 , no_sound_notes (false)
107 , pre_enter_cursor (0)
109 _note_group->raise_to_top();
110 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
112 connect_to_diskstream ();
115 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
116 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
117 TimeAxisViewItem::Visibility visibility)
118 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
120 , _last_channel_selection(0xFFFF)
121 , _model_name(string())
122 , _custom_device_mode(string())
124 , _note_group(new ArdourCanvas::Group(*parent))
125 , _note_diff_command (0)
128 , _step_edit_cursor (0)
129 , _step_edit_cursor_width (1.0)
130 , _step_edit_cursor_position (0.0)
131 , _channel_selection_scoped_note (0)
132 , _temporary_note_group (0)
135 , _sort_needed (true)
136 , _optimization_iterator (_events.end())
138 , no_sound_notes (false)
142 _note_group->raise_to_top();
143 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
145 connect_to_diskstream ();
148 MidiRegionView::MidiRegionView (const MidiRegionView& other)
149 : sigc::trackable(other)
152 , _last_channel_selection(0xFFFF)
153 , _model_name(string())
154 , _custom_device_mode(string())
156 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
157 , _note_diff_command (0)
160 , _step_edit_cursor (0)
161 , _step_edit_cursor_width (1.0)
162 , _step_edit_cursor_position (0.0)
163 , _channel_selection_scoped_note (0)
164 , _temporary_note_group (0)
167 , _sort_needed (true)
168 , _optimization_iterator (_events.end())
170 , no_sound_notes (false)
177 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
178 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
183 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
184 : RegionView (other, boost::shared_ptr<Region> (region))
186 , _last_channel_selection(0xFFFF)
187 , _model_name(string())
188 , _custom_device_mode(string())
190 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
191 , _note_diff_command (0)
194 , _step_edit_cursor (0)
195 , _step_edit_cursor_width (1.0)
196 , _step_edit_cursor_position (0.0)
197 , _channel_selection_scoped_note (0)
198 , _temporary_note_group (0)
201 , _sort_needed (true)
202 , _optimization_iterator (_events.end())
204 , no_sound_notes (false)
211 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
212 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
218 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
220 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
222 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
223 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
227 midi_region()->midi_source(0)->load_model();
230 _model = midi_region()->midi_source(0)->model();
231 _enable_display = false;
233 RegionView::init (basic_color, false);
235 compute_colors (basic_color);
237 set_height (trackview.current_height());
240 region_sync_changed ();
241 region_resized (ARDOUR::bounds_change);
244 reset_width_dependent_items (_pixel_width);
248 _enable_display = true;
251 display_model (_model);
255 group->raise_to_top();
256 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
258 midi_view()->signal_channel_mode_changed().connect(
259 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
261 midi_view()->signal_midi_patch_settings_changed().connect(
262 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
264 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
266 connect_to_diskstream ();
270 MidiRegionView::connect_to_diskstream ()
272 midi_view()->midi_track()->DataRecorded.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::data_recorded, this, _1, _2), gui_context ());
276 MidiRegionView::canvas_event(GdkEvent* ev)
279 case GDK_ENTER_NOTIFY:
280 case GDK_LEAVE_NOTIFY:
281 _last_event_x = ev->crossing.x;
282 _last_event_y = ev->crossing.y;
284 case GDK_MOTION_NOTIFY:
285 _last_event_x = ev->motion.x;
286 _last_event_y = ev->motion.y;
292 if (!trackview.editor().internal_editing()) {
296 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
297 to its items, which means that ev->type == GDK_SCROLL will never be seen
302 return scroll (&ev->scroll);
305 return key_press (&ev->key);
307 case GDK_KEY_RELEASE:
308 return key_release (&ev->key);
310 case GDK_BUTTON_PRESS:
311 return button_press (&ev->button);
313 case GDK_2BUTTON_PRESS:
316 case GDK_BUTTON_RELEASE:
317 return button_release (&ev->button);
319 case GDK_ENTER_NOTIFY:
320 return enter_notify (&ev->crossing);
322 case GDK_LEAVE_NOTIFY:
323 return leave_notify (&ev->crossing);
325 case GDK_MOTION_NOTIFY:
326 return motion (&ev->motion);
336 MidiRegionView::remove_ghost_note ()
343 MidiRegionView::enter_notify (GdkEventCrossing* ev)
345 trackview.editor().MouseModeChanged.connect (
346 _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
349 Keyboard::magic_widget_grab_focus();
352 if (trackview.editor().current_mouse_mode() == MouseRange) {
353 create_ghost_note (ev->x, ev->y);
360 MidiRegionView::leave_notify (GdkEventCrossing*)
362 _mouse_mode_connection.disconnect ();
364 trackview.editor().hide_verbose_canvas_cursor ();
365 remove_ghost_note ();
370 MidiRegionView::mouse_mode_changed ()
372 if (trackview.editor().current_mouse_mode() == MouseRange && trackview.editor().internal_editing()) {
373 create_ghost_note (_last_event_x, _last_event_y);
375 remove_ghost_note ();
376 trackview.editor().hide_verbose_canvas_cursor ();
381 MidiRegionView::button_press (GdkEventButton* ev)
386 group->w2i (_last_x, _last_y);
388 if (_mouse_state != SelectTouchDragging) {
390 _pressed_button = ev->button;
391 _mouse_state = Pressed;
396 _pressed_button = ev->button;
402 MidiRegionView::button_release (GdkEventButton* ev)
404 double event_x, event_y;
405 framepos_t event_frame = 0;
410 group->w2i(event_x, event_y);
411 group->ungrab(ev->time);
413 event_frame = trackview.editor().pixel_to_frame(event_x);
415 switch (_mouse_state) {
416 case Pressed: // Clicked
418 switch (trackview.editor().current_mouse_mode()) {
424 if (Keyboard::is_insert_note_event(ev)){
426 double event_x, event_y;
430 group->w2i(event_x, event_y);
433 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
439 create_note_at (event_x, event_y, beats, true);
447 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
453 create_note_at (event_x, event_y, beats, true);
464 case SelectRectDragging: // Select drag done
471 case AddDragging: // Add drag done
475 if (Keyboard::is_insert_note_event(ev) || trackview.editor().current_mouse_mode() == MouseRange){
477 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
479 const double x = _drag_rect->property_x1();
480 const double length = trackview.editor().pixel_to_frame (_drag_rect->property_x2() - _drag_rect->property_x1());
482 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), true);
489 create_ghost_note (ev->x, ev->y);
499 MidiRegionView::motion (GdkEventMotion* ev)
501 double event_x, event_y;
502 framepos_t event_frame = 0;
506 group->w2i(event_x, event_y);
508 // convert event_x to global frame
509 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
510 trackview.editor().snap_to(event_frame);
512 // convert event_frame back to local coordinates relative to position
513 event_frame -= _region->position();
515 if (!_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
516 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())
517 && _mouse_state != AddDragging){
519 create_ghost_note (ev->x, ev->y);
521 else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
522 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())){
524 update_ghost_note (ev->x, ev->y);
526 else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange){
531 trackview.editor().hide_verbose_canvas_cursor ();
533 else if (_ghost_note && trackview.editor().current_mouse_mode() == MouseRange) {
534 update_ghost_note (ev->x, ev->y);
537 /* any motion immediately hides velocity text that may have been visible */
539 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
540 (*i)->hide_velocity ();
543 switch (_mouse_state) {
544 case Pressed: // Maybe start a drag, if we've moved a bit
546 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
547 /* no appreciable movement since the button was pressed */
552 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject
553 && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
555 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
556 Gdk::Cursor(Gdk::FLEUR), ev->time);
560 _drag_start_x = event_x;
561 _drag_start_y = event_y;
563 _drag_rect = new ArdourCanvas::SimpleRect(*group);
564 _drag_rect->property_x1() = event_x;
565 _drag_rect->property_y1() = event_y;
566 _drag_rect->property_x2() = event_x;
567 _drag_rect->property_y2() = event_y;
568 _drag_rect->property_outline_what() = 0xFF;
569 _drag_rect->property_outline_color_rgba()
570 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
571 _drag_rect->property_fill_color_rgba()
572 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
574 _mouse_state = SelectRectDragging;
577 // Add note drag start
578 } else if (trackview.editor().internal_editing()) {
583 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
584 Gdk::Cursor(Gdk::FLEUR), ev->time);
588 _drag_start_x = event_x;
589 _drag_start_y = event_y;
591 _drag_rect = new ArdourCanvas::SimpleRect(*group);
592 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
594 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
595 midi_stream_view()->y_to_note(event_y));
596 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
597 _drag_rect->property_y2() = _drag_rect->property_y1()
598 + floor(midi_stream_view()->note_height());
599 _drag_rect->property_outline_what() = 0xFF;
600 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
601 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
603 _mouse_state = AddDragging;
610 trackview.editor().hide_verbose_canvas_cursor ();
618 case SelectRectDragging: // Select drag motion
619 case AddDragging: // Add note drag motion
624 GdkModifierType state;
625 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
630 if (_mouse_state == AddDragging){
631 event_x = trackview.editor().frame_to_pixel(event_frame);
636 if (event_x > _drag_start_x){
637 _drag_rect->property_x2() = event_x;
640 _drag_rect->property_x1() = event_x;
644 if (_drag_rect && _mouse_state == SelectRectDragging) {
646 if (event_y > _drag_start_y){
647 _drag_rect->property_y2() = event_y;
650 _drag_rect->property_y1() = event_y;
653 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
659 case SelectTouchDragging:
671 MidiRegionView::scroll (GdkEventScroll* ev)
673 if (_selection.empty()) {
677 trackview.editor().hide_verbose_canvas_cursor ();
679 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
681 if (ev->direction == GDK_SCROLL_UP) {
682 change_velocities (true, fine, false);
683 } else if (ev->direction == GDK_SCROLL_DOWN) {
684 change_velocities (false, fine, false);
690 MidiRegionView::key_press (GdkEventKey* ev)
692 /* since GTK bindings are generally activated on press, and since
693 detectable auto-repeat is the name of the game and only sends
694 repeated presses, carry out key actions at key press, not release.
697 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
698 _mouse_state = SelectTouchDragging;
701 } else if (ev->keyval == GDK_Escape) {
705 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
707 bool start = (ev->keyval == GDK_comma);
708 bool end = (ev->keyval == GDK_period);
709 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
710 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
712 change_note_lengths (fine, shorter, 0.0, start, end);
716 } else if (ev->keyval == GDK_Delete) {
721 } else if (ev->keyval == GDK_Tab) {
723 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
724 goto_previous_note ();
730 } else if (ev->keyval == GDK_Up) {
732 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
733 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
735 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
736 change_velocities (true, fine, allow_smush);
738 transpose (true, fine, allow_smush);
742 } else if (ev->keyval == GDK_Down) {
744 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
745 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
747 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
748 change_velocities (false, fine, allow_smush);
750 transpose (false, fine, allow_smush);
754 } else if (ev->keyval == GDK_Left) {
759 } else if (ev->keyval == GDK_Right) {
764 } else if (ev->keyval == GDK_Control_L) {
773 MidiRegionView::key_release (GdkEventKey* ev)
775 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
783 MidiRegionView::show_list_editor ()
786 _list_editor = new MidiListEditor (trackview.session(), midi_region());
788 _list_editor->present ();
791 /** Add a note to the model, and the view, at a canvas (click) coordinate.
792 * \param x horizontal position in pixels
793 * \param y vertical position in pixels
794 * \param length duration of the note in beats, which will be snapped to the grid
795 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
798 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
800 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
801 MidiStreamView* const view = mtv->midi_view();
803 double note = view->y_to_note(y);
806 assert(note <= 127.0);
808 // Start of note in frames relative to region start
809 framepos_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
810 assert(start_frames >= 0);
813 length = frames_to_beats(
814 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
816 assert (length != 0);
819 length = frames_to_beats (beats_to_frames (length) - 1);
822 const boost::shared_ptr<NoteType> new_note (new NoteType (get_channel_for_add (),
823 frames_to_beats(start_frames + _region->start()), length,
824 (uint8_t)note, 0x40));
826 if (_model->contains (new_note)) {
830 view->update_note_range(new_note->note());
832 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note");
834 _model->apply_command(*trackview.session(), cmd);
836 play_midi_note (new_note);
840 MidiRegionView::clear_events()
845 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
846 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
851 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
856 _patch_changes.clear();
858 _optimization_iterator = _events.end();
862 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
866 content_connection.disconnect ();
867 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
871 if (_enable_display) {
877 MidiRegionView::start_note_diff_command (string name)
879 if (!_note_diff_command) {
880 _note_diff_command = _model->new_note_diff_command (name);
885 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
887 if (_note_diff_command) {
888 _note_diff_command->add (note);
891 _marked_for_selection.insert(note);
894 _marked_for_velocity.insert(note);
899 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
901 if (_note_diff_command && ev->note()) {
902 _note_diff_command->remove(ev->note());
907 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
908 MidiModel::NoteDiffCommand::Property property,
911 if (_note_diff_command) {
912 _note_diff_command->change (ev->note(), property, val);
917 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
918 MidiModel::NoteDiffCommand::Property property,
919 Evoral::MusicalTime val)
921 if (_note_diff_command) {
922 _note_diff_command->change (ev->note(), property, val);
927 MidiRegionView::apply_diff ()
931 if (!_note_diff_command) {
935 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
936 // Mark all selected notes for selection when model reloads
937 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
938 _marked_for_selection.insert((*i)->note());
942 _model->apply_command(*trackview.session(), _note_diff_command);
943 _note_diff_command = 0;
944 midi_view()->midi_track()->playlist_modified();
947 _marked_for_selection.clear();
950 _marked_for_velocity.clear();
954 MidiRegionView::apply_diff_as_subcommand ()
958 if (!_note_diff_command) {
962 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
963 // Mark all selected notes for selection when model reloads
964 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
965 _marked_for_selection.insert((*i)->note());
969 _model->apply_command_as_subcommand(*trackview.session(), _note_diff_command);
970 _note_diff_command = 0;
971 midi_view()->midi_track()->playlist_modified();
974 _marked_for_selection.clear();
976 _marked_for_velocity.clear();
981 MidiRegionView::abort_command()
983 delete _note_diff_command;
984 _note_diff_command = 0;
989 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
991 if (_optimization_iterator != _events.end()) {
992 ++_optimization_iterator;
995 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
996 return *_optimization_iterator;
999 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1000 if ((*_optimization_iterator)->note() == note) {
1001 return *_optimization_iterator;
1009 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1011 MidiModel::Notes notes;
1012 _model->get_notes (notes, op, val, chan_mask);
1014 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1015 CanvasNoteEvent* cne = find_canvas_note (*n);
1023 MidiRegionView::redisplay_model()
1025 // Don't redisplay the model if we're currently recording and displaying that
1026 if (_active_notes) {
1031 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
1035 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1036 (*i)->invalidate ();
1039 MidiModel::ReadLock lock(_model->read_lock());
1041 MidiModel::Notes& notes (_model->notes());
1042 _optimization_iterator = _events.begin();
1044 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1046 boost::shared_ptr<NoteType> note (*n);
1047 CanvasNoteEvent* cne;
1050 if (note_in_region_range (note, visible)) {
1052 if ((cne = find_canvas_note (note)) != 0) {
1059 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1061 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1073 add_note (note, visible);
1078 if ((cne = find_canvas_note (note)) != 0) {
1086 /* remove note items that are no longer valid */
1088 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1089 if (!(*i)->valid ()) {
1091 i = _events.erase (i);
1097 _patch_changes.clear();
1101 display_patch_changes ();
1103 _marked_for_selection.clear ();
1104 _marked_for_velocity.clear ();
1106 /* we may have caused _events to contain things out of order (e.g. if a note
1107 moved earlier or later). we don't generally need them in time order, but
1108 make a note that a sort is required for those cases that require it.
1111 _sort_needed = true;
1115 MidiRegionView::display_patch_changes ()
1117 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1118 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1120 for (uint8_t i = 0; i < 16; ++i) {
1121 if (chn_mask & (1<<i)) {
1122 display_patch_changes_on_channel (i);
1128 MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
1130 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1132 if ((*i)->channel() != channel) {
1136 MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
1138 boost::shared_ptr<MIDI::Name::Patch> patch =
1139 MIDI::Name::MidiPatchManager::instance().find_patch(
1140 _model_name, _custom_device_mode, channel, patch_key);
1143 add_canvas_patch_change (*i, patch->name());
1146 // program_number is zero-based: convert to one-based
1147 snprintf (buf, 4, "%d", (*i)->program() + 1);
1148 add_canvas_patch_change (*i, buf);
1154 MidiRegionView::display_sysexes()
1156 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1157 Evoral::MusicalTime time = (*i)->time();
1162 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1163 str << int((*i)->buffer()[b]);
1164 if (b != (*i)->size() -1) {
1168 string text = str.str();
1170 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1172 double height = midi_stream_view()->contents_height();
1174 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1175 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1177 // Show unless patch change is beyond the region bounds
1178 if (time - _region->start() >= _region->length() || time < _region->start()) {
1184 _sys_exes.push_back(sysex);
1189 MidiRegionView::~MidiRegionView ()
1191 in_destructor = true;
1193 trackview.editor().hide_verbose_canvas_cursor ();
1195 note_delete_connection.disconnect ();
1197 delete _list_editor;
1199 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1201 if (_active_notes) {
1209 delete _note_diff_command;
1210 delete _step_edit_cursor;
1211 delete _temporary_note_group;
1215 MidiRegionView::region_resized (const PropertyChange& what_changed)
1217 RegionView::region_resized(what_changed);
1219 if (what_changed.contains (ARDOUR::Properties::position)) {
1220 set_duration(_region->length(), 0);
1221 if (_enable_display) {
1228 MidiRegionView::reset_width_dependent_items (double pixel_width)
1230 RegionView::reset_width_dependent_items(pixel_width);
1231 assert(_pixel_width == pixel_width);
1233 if (_enable_display) {
1237 move_step_edit_cursor (_step_edit_cursor_position);
1238 set_step_edit_cursor_width (_step_edit_cursor_width);
1242 MidiRegionView::set_height (double height)
1244 static const double FUDGE = 2.0;
1245 const double old_height = _height;
1246 RegionView::set_height(height);
1247 _height = height - FUDGE;
1249 apply_note_range(midi_stream_view()->lowest_note(),
1250 midi_stream_view()->highest_note(),
1251 height != old_height + FUDGE);
1254 name_pixbuf->raise_to_top();
1257 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1258 (*x)->set_height (midi_stream_view()->contents_height());
1261 if (_step_edit_cursor) {
1262 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1267 /** Apply the current note range from the stream view
1268 * by repositioning/hiding notes as necessary
1271 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1273 if (!_enable_display) {
1277 if (!force && _current_range_min == min && _current_range_max == max) {
1281 _current_range_min = min;
1282 _current_range_max = max;
1284 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1285 CanvasNoteEvent* event = *i;
1286 boost::shared_ptr<NoteType> note (event->note());
1288 if (note->note() < _current_range_min ||
1289 note->note() > _current_range_max) {
1295 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1297 const double y1 = midi_stream_view()->note_to_y(note->note());
1298 const double y2 = y1 + floor(midi_stream_view()->note_height());
1300 cnote->property_y1() = y1;
1301 cnote->property_y2() = y2;
1303 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1305 const double diamond_size = update_hit (chit);
1307 chit->set_height (diamond_size);
1313 MidiRegionView::add_ghost (TimeAxisView& tv)
1317 double unit_position = _region->position () / samples_per_unit;
1318 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1319 MidiGhostRegion* ghost;
1321 if (mtv && mtv->midi_view()) {
1322 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1323 to allow having midi notes on top of note lines and waveforms.
1325 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1327 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1330 ghost->set_height ();
1331 ghost->set_duration (_region->length() / samples_per_unit);
1332 ghosts.push_back (ghost);
1334 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1335 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1336 ghost->add_note(note);
1340 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1346 /** Begin tracking note state for successive calls to add_event
1349 MidiRegionView::begin_write()
1351 assert(!_active_notes);
1352 _active_notes = new CanvasNote*[128];
1353 for (unsigned i=0; i < 128; ++i) {
1354 _active_notes[i] = 0;
1359 /** Destroy note state for add_event
1362 MidiRegionView::end_write()
1364 delete[] _active_notes;
1366 _marked_for_selection.clear();
1367 _marked_for_velocity.clear();
1371 /** Resolve an active MIDI note (while recording).
1374 MidiRegionView::resolve_note(uint8_t note, double end_time)
1376 if (midi_view()->note_mode() != Sustained) {
1380 if (_active_notes && _active_notes[note]) {
1382 const framepos_t end_time_frames = beats_to_frames(end_time);
1384 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1385 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1386 _active_notes[note] = 0;
1391 /** Extend active notes to rightmost edge of region (if length is changed)
1394 MidiRegionView::extend_active_notes()
1396 if (!_active_notes) {
1400 for (unsigned i=0; i < 128; ++i) {
1401 if (_active_notes[i]) {
1402 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1409 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1411 if (no_sound_notes || !trackview.editor().sound_notes()) {
1415 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1417 if (!route_ui || !route_ui->midi_track()) {
1421 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1427 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1429 if (no_sound_notes || !trackview.editor().sound_notes()) {
1433 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1435 if (!route_ui || !route_ui->midi_track()) {
1439 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1441 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1450 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1452 const framepos_t note_start_frames = beats_to_frames(note->time());
1454 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1455 (note_start_frames < _region->start());
1457 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1458 (note->note() <= midi_stream_view()->highest_note());
1464 MidiRegionView::update_note (CanvasNote* ev)
1466 boost::shared_ptr<NoteType> note = ev->note();
1468 const framepos_t note_start_frames = beats_to_frames(note->time());
1470 /* trim note display to not overlap the end of its region */
1471 const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1473 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1474 const double y1 = midi_stream_view()->note_to_y(note->note());
1475 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1477 ev->property_x1() = x;
1478 ev->property_y1() = y1;
1480 if (note->length() > 0) {
1481 ev->property_x2() = note_endpixel;
1483 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1486 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1488 if (note->length() == 0) {
1489 if (_active_notes) {
1490 assert(note->note() < 128);
1491 // If this note is already active there's a stuck note,
1492 // finish the old note rectangle
1493 if (_active_notes[note->note()]) {
1494 CanvasNote* const old_rect = _active_notes[note->note()];
1495 boost::shared_ptr<NoteType> old_note = old_rect->note();
1496 old_rect->property_x2() = x;
1497 old_rect->property_outline_what() = (guint32) 0xF;
1499 _active_notes[note->note()] = ev;
1501 /* outline all but right edge */
1502 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1504 /* outline all edges */
1505 ev->property_outline_what() = (guint32) 0xF;
1510 MidiRegionView::update_hit (CanvasHit* ev)
1512 boost::shared_ptr<NoteType> note = ev->note();
1514 const framepos_t note_start_frames = beats_to_frames(note->time());
1515 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1516 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1517 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1521 return diamond_size;
1524 /** Add a MIDI note to the view (with length).
1526 * If in sustained mode, notes with length 0 will be considered active
1527 * notes, and resolve_note should be called when the corresponding note off
1528 * event arrives, to properly display the note.
1531 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1533 CanvasNoteEvent* event = 0;
1535 assert(note->time() >= 0);
1536 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1538 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1540 if (midi_view()->note_mode() == Sustained) {
1542 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1544 update_note (ev_rect);
1548 MidiGhostRegion* gr;
1550 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1551 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1552 gr->add_note(ev_rect);
1556 } else if (midi_view()->note_mode() == Percussive) {
1558 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1560 CanvasHit* ev_diamond = new CanvasHit(*this, *_note_group, diamond_size, note);
1562 update_hit (ev_diamond);
1571 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1572 note_selected(event, true);
1575 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1576 event->show_velocity();
1579 event->on_channel_selection_change(_last_channel_selection);
1580 _events.push_back(event);
1589 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1590 MidiStreamView* const view = mtv->midi_view();
1592 view->update_note_range(note->note());
1596 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1597 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1599 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1601 /* potentially extend region to hold new note */
1603 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1604 framepos_t region_end = _region->position() + _region->length() - 1;
1606 if (end_frame > region_end) {
1607 _region->set_length (end_frame - _region->position(), this);
1610 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1611 MidiStreamView* const view = mtv->midi_view();
1613 view->update_note_range(new_note->note());
1615 _marked_for_selection.clear ();
1618 start_note_diff_command (_("step add"));
1619 note_diff_add_note (new_note, true, false);
1622 // last_step_edit_note = new_note;
1626 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1628 change_note_lengths (false, false, beats, false, true);
1632 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
1634 assert (patch->time() >= 0);
1636 const double x = trackview.editor().frame_to_pixel (beats_to_frames (patch->time()));
1638 double const height = midi_stream_view()->contents_height();
1640 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1641 new CanvasPatchChange(*this, *_note_group,
1646 _custom_device_mode,
1650 // Show unless patch change is beyond the region bounds
1651 if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1652 patch_change->hide();
1654 patch_change->show();
1657 _patch_changes.push_back (patch_change);
1661 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1663 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1664 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1668 if (i != _model->patch_changes().end()) {
1669 key.msb = (*i)->bank_msb ();
1670 key.lsb = (*i)->bank_lsb ();
1671 key.program_number = (*i)->program ();
1673 key.msb = key.lsb = key.program_number = 0;
1676 assert (key.is_sane());
1681 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1683 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1685 if (pc.patch()->program() != new_patch.program_number) {
1686 c->change_program (pc.patch (), new_patch.program_number);
1689 int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1690 if (pc.patch()->bank() != new_bank) {
1691 c->change_bank (pc.patch (), new_bank);
1694 _model->apply_command (*trackview.session(), c);
1696 _patch_changes.clear ();
1697 display_patch_changes ();
1701 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1703 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1705 if (old_change->time() != new_change.time()) {
1706 c->change_time (old_change, new_change.time());
1709 if (old_change->channel() != new_change.channel()) {
1710 c->change_channel (old_change, new_change.channel());
1713 if (old_change->program() != new_change.program()) {
1714 c->change_program (old_change, new_change.program());
1717 if (old_change->bank() != new_change.bank()) {
1718 c->change_bank (old_change, new_change.bank());
1721 _model->apply_command (*trackview.session(), c);
1723 _patch_changes.clear ();
1724 display_patch_changes ();
1727 /** Add a patch change to the region.
1728 * @param t Time in frames relative to region position
1729 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1730 * get_channel_for_add())
1733 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1735 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1736 c->add (MidiModel::PatchChangePtr (
1737 new Evoral::PatchChange<Evoral::MusicalTime> (
1738 frames_to_beats (t + midi_region()->start()), get_channel_for_add(), patch.program(), patch.bank()
1742 _model->apply_command (*trackview.session(), c);
1744 _patch_changes.clear ();
1745 display_patch_changes ();
1749 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1751 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1752 c->change_time (pc.patch (), t);
1753 _model->apply_command (*trackview.session(), c);
1755 _patch_changes.clear ();
1756 display_patch_changes ();
1760 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1762 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1763 c->remove (pc->patch ());
1764 _model->apply_command (*trackview.session(), c);
1766 _patch_changes.clear ();
1767 display_patch_changes ();
1771 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1773 if (patch.patch()->program() < 127) {
1774 MIDI::Name::PatchPrimaryKey key;
1775 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1776 key.program_number++;
1777 change_patch_change (patch, key);
1782 MidiRegionView::next_patch (CanvasPatchChange& patch)
1784 if (patch.patch()->program() > 0) {
1785 MIDI::Name::PatchPrimaryKey key;
1786 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1787 key.program_number--;
1788 change_patch_change (patch, key);
1793 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1795 if (_selection.empty()) {
1799 if (_selection.erase (cne) > 0) {
1800 cerr << "Erased a CNE from selection\n";
1805 MidiRegionView::delete_selection()
1807 if (_selection.empty()) {
1811 start_note_diff_command (_("delete selection"));
1813 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1814 if ((*i)->selected()) {
1815 _note_diff_command->remove((*i)->note());
1825 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1827 start_note_diff_command (_("delete note"));
1828 _note_diff_command->remove (n);
1831 trackview.editor().hide_verbose_canvas_cursor ();
1835 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1837 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1838 if ((*i)->selected() && (*i) != ev) {
1839 (*i)->set_selected(false);
1840 (*i)->hide_velocity();
1848 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1850 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1853 Selection::iterator tmp = i;
1856 (*i)->set_selected (false);
1857 _selection.erase (i);
1866 /* don't bother with removing this regionview from the editor selection,
1867 since we're about to add another note, and thus put/keep this
1868 regionview in the editor selection.
1871 if (!ev->selected()) {
1872 add_to_selection (ev);
1877 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1879 uint8_t low_note = 127;
1880 uint8_t high_note = 0;
1881 MidiModel::Notes& notes (_model->notes());
1882 _optimization_iterator = _events.begin();
1888 if (extend && _selection.empty()) {
1894 /* scan existing selection to get note range */
1896 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1897 if ((*i)->note()->note() < low_note) {
1898 low_note = (*i)->note()->note();
1900 if ((*i)->note()->note() > high_note) {
1901 high_note = (*i)->note()->note();
1905 low_note = min (low_note, notenum);
1906 high_note = max (high_note, notenum);
1909 no_sound_notes = true;
1911 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1913 boost::shared_ptr<NoteType> note (*n);
1914 CanvasNoteEvent* cne;
1915 bool select = false;
1917 if (((1 << note->channel()) & channel_mask) != 0) {
1919 if ((note->note() >= low_note && note->note() <= high_note)) {
1922 } else if (note->note() == notenum) {
1928 if ((cne = find_canvas_note (note)) != 0) {
1929 // extend is false because we've taken care of it,
1930 // since it extends by time range, not pitch.
1931 note_selected (cne, add, false);
1935 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1939 no_sound_notes = false;
1943 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1945 MidiModel::Notes& notes (_model->notes());
1946 _optimization_iterator = _events.begin();
1948 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1950 boost::shared_ptr<NoteType> note (*n);
1951 CanvasNoteEvent* cne;
1953 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1954 if ((cne = find_canvas_note (note)) != 0) {
1955 if (cne->selected()) {
1956 note_deselected (cne);
1958 note_selected (cne, true, false);
1966 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1969 clear_selection_except(ev);
1974 if (!ev->selected()) {
1975 add_to_selection (ev);
1979 /* find end of latest note selected, select all between that and the start of "ev" */
1981 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1982 Evoral::MusicalTime latest = 0;
1984 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1985 if ((*i)->note()->end_time() > latest) {
1986 latest = (*i)->note()->end_time();
1988 if ((*i)->note()->time() < earliest) {
1989 earliest = (*i)->note()->time();
1993 if (ev->note()->end_time() > latest) {
1994 latest = ev->note()->end_time();
1997 if (ev->note()->time() < earliest) {
1998 earliest = ev->note()->time();
2001 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2003 /* find notes entirely within OR spanning the earliest..latest range */
2005 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2006 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2007 add_to_selection (*i);
2011 /* if events were guaranteed to be time sorted, we could do this.
2012 but as of sept 10th 2009, they no longer are.
2015 if ((*i)->note()->time() > latest) {
2024 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2026 remove_from_selection (ev);
2030 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
2040 // TODO: Make this faster by storing the last updated selection rect, and only
2041 // adjusting things that are in the area that appears/disappeared.
2042 // We probably need a tree to be able to find events in O(log(n)) time.
2044 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2046 /* check if any corner of the note is inside the rect
2049 1) this is computing "touched by", not "contained by" the rect.
2050 2) this does not require that events be sorted in time.
2053 const double ix1 = (*i)->x1();
2054 const double ix2 = (*i)->x2();
2055 const double iy1 = (*i)->y1();
2056 const double iy2 = (*i)->y2();
2058 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2059 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2060 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2061 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2064 if (!(*i)->selected()) {
2065 add_to_selection (*i);
2067 } else if ((*i)->selected()) {
2068 // Not inside rectangle
2069 remove_from_selection (*i);
2075 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2077 Selection::iterator i = _selection.find (ev);
2079 if (i != _selection.end()) {
2080 _selection.erase (i);
2083 ev->set_selected (false);
2084 ev->hide_velocity ();
2086 if (_selection.empty()) {
2087 PublicEditor& editor (trackview.editor());
2088 editor.get_selection().remove (this);
2093 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2095 bool add_mrv_selection = false;
2097 if (_selection.empty()) {
2098 add_mrv_selection = true;
2101 if (_selection.insert (ev).second) {
2102 ev->set_selected (true);
2103 play_midi_note ((ev)->note());
2106 if (add_mrv_selection) {
2107 PublicEditor& editor (trackview.editor());
2108 editor.get_selection().add (this);
2113 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2115 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2116 PossibleChord to_play;
2117 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2119 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2120 if ((*i)->note()->time() < earliest) {
2121 earliest = (*i)->note()->time();
2125 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2126 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2127 to_play.push_back ((*i)->note());
2129 (*i)->move_event(dx, dy);
2132 if (dy && !_selection.empty() && !no_sound_notes && trackview.editor().sound_notes()) {
2134 if (to_play.size() > 1) {
2136 PossibleChord shifted;
2138 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2139 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2140 moved_note->set_note (moved_note->note() + cumulative_dy);
2141 shifted.push_back (moved_note);
2144 play_midi_chord (shifted);
2146 } else if (!to_play.empty()) {
2148 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2149 moved_note->set_note (moved_note->note() + cumulative_dy);
2150 play_midi_note (moved_note);
2156 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2158 assert (!_selection.empty());
2160 uint8_t lowest_note_in_selection = 127;
2161 uint8_t highest_note_in_selection = 0;
2162 uint8_t highest_note_difference = 0;
2164 // find highest and lowest notes first
2166 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2167 uint8_t pitch = (*i)->note()->note();
2168 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2169 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2173 cerr << "dnote: " << (int) dnote << endl;
2174 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2175 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2176 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2177 << int(highest_note_in_selection) << endl;
2178 cerr << "selection size: " << _selection.size() << endl;
2179 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2182 // Make sure the note pitch does not exceed the MIDI standard range
2183 if (highest_note_in_selection + dnote > 127) {
2184 highest_note_difference = highest_note_in_selection - 127;
2187 start_note_diff_command (_("move notes"));
2189 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2191 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2197 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2199 uint8_t original_pitch = (*i)->note()->note();
2200 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2202 // keep notes in standard midi range
2203 clamp_to_0_127(new_pitch);
2205 // keep original pitch if note is dragged outside valid midi range
2206 if ((original_pitch != 0 && new_pitch == 0)
2207 || (original_pitch != 127 && new_pitch == 127)) {
2208 new_pitch = original_pitch;
2211 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2212 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2214 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2219 // care about notes being moved beyond the upper/lower bounds on the canvas
2220 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2221 highest_note_in_selection > midi_stream_view()->highest_note()) {
2222 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2227 MidiRegionView::snap_pixel_to_frame(double x)
2229 PublicEditor& editor = trackview.editor();
2230 // x is region relative, convert it to global absolute frames
2231 framepos_t frame = editor.pixel_to_frame(x) + _region->position();
2232 editor.snap_to(frame);
2233 return frame - _region->position(); // convert back to region relative
2237 MidiRegionView::snap_frame_to_frame(framepos_t x)
2239 PublicEditor& editor = trackview.editor();
2240 // x is region relative, convert it to global absolute frames
2241 framepos_t frame = x + _region->position();
2242 editor.snap_to(frame);
2243 return frame - _region->position(); // convert back to region relative
2247 MidiRegionView::snap_to_pixel(double x)
2249 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2253 MidiRegionView::get_position_pixels()
2255 framepos_t region_frame = get_position();
2256 return trackview.editor().frame_to_pixel(region_frame);
2260 MidiRegionView::get_end_position_pixels()
2262 framepos_t frame = get_position() + get_duration ();
2263 return trackview.editor().frame_to_pixel(frame);
2267 MidiRegionView::beats_to_frames(double beats) const
2269 return _time_converter.to(beats);
2273 MidiRegionView::frames_to_beats(framepos_t frames) const
2275 return _time_converter.from(frames);
2279 MidiRegionView::begin_resizing (bool /*at_front*/)
2281 _resize_data.clear();
2283 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2284 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2286 // only insert CanvasNotes into the map
2288 NoteResizeData *resize_data = new NoteResizeData();
2289 resize_data->canvas_note = note;
2291 // create a new SimpleRect from the note which will be the resize preview
2292 SimpleRect *resize_rect = new SimpleRect(
2293 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2295 // calculate the colors: get the color settings
2296 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2297 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2300 // make the resize preview notes more transparent and bright
2301 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2303 // calculate color based on note velocity
2304 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2305 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2309 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2310 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2312 resize_data->resize_rect = resize_rect;
2313 _resize_data.push_back(resize_data);
2318 /** Update resizing notes while user drags.
2319 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2320 * @param at_front which end of the note (true == note on, false == note off)
2321 * @param delta_x change in mouse position since the start of the drag
2322 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2323 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2324 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2325 * as the \a primary note.
2328 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2330 bool cursor_set = false;
2332 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2333 SimpleRect* resize_rect = (*i)->resize_rect;
2334 CanvasNote* canvas_note = (*i)->canvas_note;
2339 current_x = canvas_note->x1() + delta_x;
2341 current_x = primary->x1() + delta_x;
2345 current_x = canvas_note->x2() + delta_x;
2347 current_x = primary->x2() + delta_x;
2352 resize_rect->property_x1() = snap_to_pixel(current_x);
2353 resize_rect->property_x2() = canvas_note->x2();
2355 resize_rect->property_x2() = snap_to_pixel(current_x);
2356 resize_rect->property_x1() = canvas_note->x1();
2362 beats = snap_pixel_to_frame (current_x);
2363 beats = frames_to_beats (beats);
2368 if (beats < canvas_note->note()->end_time()) {
2369 len = canvas_note->note()->time() - beats;
2370 len += canvas_note->note()->length();
2375 if (beats >= canvas_note->note()->time()) {
2376 len = beats - canvas_note->note()->time();
2383 snprintf (buf, sizeof (buf), "%.3g beats", len);
2384 trackview.editor().show_verbose_canvas_cursor_with (buf);
2393 /** Finish resizing notes when the user releases the mouse button.
2394 * Parameters the same as for \a update_resizing().
2397 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2399 start_note_diff_command (_("resize notes"));
2401 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2402 CanvasNote* canvas_note = (*i)->canvas_note;
2403 SimpleRect* resize_rect = (*i)->resize_rect;
2408 current_x = canvas_note->x1() + delta_x;
2410 current_x = primary->x1() + delta_x;
2414 current_x = canvas_note->x2() + delta_x;
2416 current_x = primary->x2() + delta_x;
2420 current_x = snap_pixel_to_frame (current_x);
2421 current_x = frames_to_beats (current_x);
2423 if (at_front && current_x < canvas_note->note()->end_time()) {
2424 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2426 double len = canvas_note->note()->time() - current_x;
2427 len += canvas_note->note()->length();
2430 /* XXX convert to beats */
2431 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2436 double len = current_x - canvas_note->note()->time();
2439 /* XXX convert to beats */
2440 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2448 _resize_data.clear();
2453 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2455 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, (uint8_t) channel);
2459 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2461 uint8_t new_velocity;
2464 new_velocity = event->note()->velocity() + velocity;
2465 clamp_to_0_127(new_velocity);
2467 new_velocity = velocity;
2470 event->set_selected (event->selected()); // change color
2472 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2476 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2481 new_note = event->note()->note() + note;
2486 clamp_to_0_127 (new_note);
2487 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2491 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2493 bool change_start = false;
2494 bool change_length = false;
2495 Evoral::MusicalTime new_start = 0;
2496 Evoral::MusicalTime new_length = 0;
2498 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2500 front_delta: if positive - move the start of the note later in time (shortening it)
2501 if negative - move the start of the note earlier in time (lengthening it)
2503 end_delta: if positive - move the end of the note later in time (lengthening it)
2504 if negative - move the end of the note earlier in time (shortening it)
2508 if (front_delta < 0) {
2510 if (event->note()->time() < -front_delta) {
2513 new_start = event->note()->time() + front_delta; // moves earlier
2516 /* start moved toward zero, so move the end point out to where it used to be.
2517 Note that front_delta is negative, so this increases the length.
2520 new_length = event->note()->length() - front_delta;
2521 change_start = true;
2522 change_length = true;
2526 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2528 if (new_pos < event->note()->end_time()) {
2529 new_start = event->note()->time() + front_delta;
2530 /* start moved toward the end, so move the end point back to where it used to be */
2531 new_length = event->note()->length() - front_delta;
2532 change_start = true;
2533 change_length = true;
2540 bool can_change = true;
2541 if (end_delta < 0) {
2542 if (event->note()->length() < -end_delta) {
2548 new_length = event->note()->length() + end_delta;
2549 change_length = true;
2554 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2557 if (change_length) {
2558 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2563 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2565 Evoral::MusicalTime new_time;
2569 if (event->note()->time() < -delta) {
2572 new_time = event->note()->time() + delta;
2575 new_time = event->note()->time() + delta;
2581 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2585 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2587 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2591 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2595 if (_selection.empty()) {
2610 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2611 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2617 start_note_diff_command (_("change velocities"));
2619 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2620 Selection::iterator next = i;
2622 change_note_velocity (*i, delta, true);
2628 if (!_selection.empty()) {
2630 snprintf (buf, sizeof (buf), "Vel %d",
2631 (int) (*_selection.begin())->note()->velocity());
2632 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 10);
2638 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2640 if (_selection.empty()) {
2657 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2659 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2663 if ((int8_t) (*i)->note()->note() + delta > 127) {
2670 start_note_diff_command (_("transpose"));
2672 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2673 Selection::iterator next = i;
2675 change_note_note (*i, delta, true);
2683 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2689 /* grab the current grid distance */
2691 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2693 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2694 cerr << "Grid type not available as beats - TO BE FIXED\n";
2704 start_note_diff_command (_("change note lengths"));
2706 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2707 Selection::iterator next = i;
2710 /* note the negation of the delta for start */
2712 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2721 MidiRegionView::nudge_notes (bool forward)
2723 if (_selection.empty()) {
2727 /* pick a note as the point along the timeline to get the nudge distance.
2728 its not necessarily the earliest note, so we may want to pull the notes out
2729 into a vector and sort before using the first one.
2732 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2734 framepos_t distance;
2736 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2738 /* grid is off - use nudge distance */
2740 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2746 framepos_t next_pos = ref_point;
2749 if (max_framepos - 1 < next_pos) {
2753 if (next_pos == 0) {
2759 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2760 distance = ref_point - next_pos;
2763 if (distance == 0) {
2767 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2773 start_note_diff_command (_("nudge"));
2775 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2776 Selection::iterator next = i;
2778 change_note_time (*i, delta, true);
2786 MidiRegionView::change_channel(uint8_t channel)
2788 start_note_diff_command(_("change channel"));
2789 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2790 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2798 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2800 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2802 pre_enter_cursor = editor->get_canvas_cursor ();
2804 if (_mouse_state == SelectTouchDragging) {
2805 note_selected (ev, true);
2808 show_verbose_canvas_cursor (ev->note ());
2812 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2814 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2816 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2817 (*i)->hide_velocity ();
2820 editor->hide_verbose_canvas_cursor ();
2822 if (pre_enter_cursor) {
2823 editor->set_canvas_cursor (pre_enter_cursor);
2824 pre_enter_cursor = 0;
2829 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
2832 s << ((int) ev->patch()->program() + 1) << ":" << (ev->patch()->bank() + 1);
2833 trackview.editor().show_verbose_canvas_cursor_with (s.str().c_str(), 10, 20);
2837 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
2839 trackview.editor().hide_verbose_canvas_cursor ();
2843 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
2845 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2847 if (x_fraction > 0.0 && x_fraction < 0.25) {
2848 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
2849 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
2850 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
2852 if (pre_enter_cursor && can_set_cursor) {
2853 editor->set_canvas_cursor (pre_enter_cursor);
2859 MidiRegionView::set_frame_color()
2862 if (_selected && should_show_selection) {
2863 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2865 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2871 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2875 case FilterChannels:
2876 _force_channel = -1;
2879 _force_channel = mask;
2880 mask = 0xFFFF; // Show all notes as active (below)
2883 // Update notes for selection
2884 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2885 (*i)->on_channel_selection_change(mask);
2888 _last_channel_selection = mask;
2890 _patch_changes.clear ();
2891 display_patch_changes ();
2895 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2897 _model_name = model;
2898 _custom_device_mode = custom_device_mode;
2903 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2905 if (_selection.empty()) {
2909 PublicEditor& editor (trackview.editor());
2914 editor.get_cut_buffer().add (selection_as_cut_buffer());
2922 start_note_diff_command();
2924 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2930 note_diff_remove_note (*i);
2940 MidiRegionView::selection_as_cut_buffer () const
2944 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2945 NoteType* n = (*i)->note().get();
2946 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2949 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2956 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
2962 start_note_diff_command (_("paste"));
2964 Evoral::MusicalTime beat_delta;
2965 Evoral::MusicalTime paste_pos_beats;
2966 Evoral::MusicalTime duration;
2967 Evoral::MusicalTime end_point = 0;
2969 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2970 paste_pos_beats = frames_to_beats (pos - _region->position());
2971 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2972 paste_pos_beats = 0;
2976 for (int n = 0; n < (int) times; ++n) {
2978 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2980 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2981 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2983 /* make all newly added notes selected */
2985 note_diff_add_note (copied_note, true);
2986 end_point = copied_note->end_time();
2989 paste_pos_beats += duration;
2992 /* if we pasted past the current end of the region, extend the region */
2994 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
2995 framepos_t region_end = _region->position() + _region->length() - 1;
2997 if (end_frame > region_end) {
2999 trackview.session()->begin_reversible_command (_("paste"));
3001 _region->clear_changes ();
3002 _region->set_length (end_frame, this);
3003 trackview.session()->add_command (new StatefulDiffCommand (_region));
3009 struct EventNoteTimeEarlyFirstComparator {
3010 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3011 return a->note()->time() < b->note()->time();
3016 MidiRegionView::time_sort_events ()
3018 if (!_sort_needed) {
3022 EventNoteTimeEarlyFirstComparator cmp;
3025 _sort_needed = false;
3029 MidiRegionView::goto_next_note ()
3031 // framepos_t pos = -1;
3032 bool use_next = false;
3034 if (_events.back()->selected()) {
3038 time_sort_events ();
3040 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3041 if ((*i)->selected()) {
3044 } else if (use_next) {
3046 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3051 /* use the first one */
3053 unique_select (_events.front());
3058 MidiRegionView::goto_previous_note ()
3060 // framepos_t pos = -1;
3061 bool use_next = false;
3063 if (_events.front()->selected()) {
3067 time_sort_events ();
3069 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3070 if ((*i)->selected()) {
3073 } else if (use_next) {
3075 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3080 /* use the last one */
3082 unique_select (*(_events.rbegin()));
3086 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3088 bool had_selected = false;
3090 time_sort_events ();
3092 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3093 if ((*i)->selected()) {
3094 selected.insert ((*i)->note());
3095 had_selected = true;
3099 if (allow_all_if_none_selected && !had_selected) {
3100 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3101 selected.insert ((*i)->note());
3107 MidiRegionView::update_ghost_note (double x, double y)
3112 _note_group->w2i (x, y);
3113 framepos_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
3114 trackview.editor().snap_to (f);
3115 f -= _region->position ();
3118 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
3124 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
3126 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
3127 _ghost_note->note()->set_length (length);
3128 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3130 update_note (_ghost_note);
3132 show_verbose_canvas_cursor (_ghost_note->note ());
3136 MidiRegionView::create_ghost_note (double x, double y)
3141 boost::shared_ptr<NoteType> g (new NoteType);
3142 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3143 update_ghost_note (x, y);
3144 _ghost_note->show ();
3149 show_verbose_canvas_cursor (_ghost_note->note ());
3153 MidiRegionView::snap_changed ()
3159 create_ghost_note (_last_ghost_x, _last_ghost_y);
3163 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
3166 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
3167 Evoral::midi_note_name (n->note()).c_str(),
3169 (int) n->velocity());
3170 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 20);
3174 MidiRegionView::drop_down_keys ()
3176 _mouse_state = None;
3180 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3182 double note = midi_stream_view()->y_to_note(y);
3184 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3186 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3188 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3189 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3190 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3191 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3196 bool add_mrv_selection = false;
3198 if (_selection.empty()) {
3199 add_mrv_selection = true;
3202 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3203 if (_selection.insert (*i).second) {
3204 (*i)->set_selected (true);
3208 if (add_mrv_selection) {
3209 PublicEditor& editor (trackview.editor());
3210 editor.get_selection().add (this);
3215 MidiRegionView::color_handler ()
3217 RegionView::color_handler ();
3219 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3220 (*i)->set_selected ((*i)->selected()); // will change color
3223 /* XXX probably more to do here */
3227 MidiRegionView::enable_display (bool yn)
3229 RegionView::enable_display (yn);
3236 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3238 if (_step_edit_cursor == 0) {
3239 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3241 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3242 _step_edit_cursor->property_y1() = 0;
3243 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3244 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3245 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3248 move_step_edit_cursor (pos);
3249 _step_edit_cursor->show ();
3253 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3255 _step_edit_cursor_position = pos;
3257 if (_step_edit_cursor) {
3258 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3259 _step_edit_cursor->property_x1() = pixel;
3260 set_step_edit_cursor_width (_step_edit_cursor_width);
3265 MidiRegionView::hide_step_edit_cursor ()
3267 if (_step_edit_cursor) {
3268 _step_edit_cursor->hide ();
3273 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3275 _step_edit_cursor_width = beats;
3277 if (_step_edit_cursor) {
3278 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
3282 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3283 * @param buf Data that has been recorded.
3284 * @param w Source that this data will end up in.
3287 MidiRegionView::data_recorded (boost::shared_ptr<MidiBuffer> buf, boost::weak_ptr<MidiSource> w)
3289 if (!_active_notes) {
3290 /* we aren't actively being recorded to */
3294 boost::shared_ptr<MidiSource> src = w.lock ();
3295 if (!src || src != midi_region()->midi_source()) {
3296 /* recorded data was not destined for our source */
3300 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3301 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3303 framepos_t back = max_framepos;
3305 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3306 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3307 assert (ev.buffer ());
3309 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3311 if (ev.type() == MIDI_CMD_NOTE_ON) {
3313 boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > note (
3314 new Evoral::Note<Evoral::MusicalTime> (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3317 add_note (note, true);
3319 /* fix up our note range */
3320 if (ev.note() < _current_range_min) {
3321 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3322 } else if (ev.note() > _current_range_max) {
3323 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3326 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3327 resolve_note (ev.note (), time_beats);
3333 midi_stream_view()->check_record_layers (region(), back);
3337 MidiRegionView::trim_front_starting ()
3339 /* Reparent the note group to the region view's parent, so that it doesn't change
3340 when the region view is trimmed.
3342 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3343 _temporary_note_group->move (group->property_x(), group->property_y());
3344 _note_group->reparent (*_temporary_note_group);
3348 MidiRegionView::trim_front_ending ()
3350 _note_group->reparent (*group);
3351 delete _temporary_note_group;
3352 _temporary_note_group = 0;
3354 if (_region->start() < 0) {
3355 /* Trim drag made start time -ve; fix this */
3356 midi_region()->fix_negative_start ();
3360 /** @return channel (counted from 0) to add an event to, based on the current setting
3361 * of the channel selector.
3364 MidiRegionView::get_channel_for_add () const
3366 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3367 uint16_t const chn_mask = mtv->channel_selector().get_selected_channels();
3369 uint8_t channel = 0;
3371 /* pick the highest selected channel, unless all channels are selected,
3372 which is interpreted to mean channel 1 (zero)
3375 for (uint16_t i = 0; i < 16; ++i) {
3376 if (chn_mask & (1<<i)) {
3382 if (chn_cnt == 16) {
3390 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3392 PatchChangeDialog d (&_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3393 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3397 change_patch_change (pc->patch(), d.patch ());