2 Copyright (C) 2001-2011 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <gtkmm2ext/gtk_ui.h>
29 #include <sigc++/signal.h>
31 #include "pbd/memento_command.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "ardour/playlist.h"
35 #include "ardour/tempo.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_model.h"
39 #include "ardour/midi_patch_manager.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIParameters.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "automation_region_view.h"
48 #include "automation_time_axis.h"
49 #include "canvas-hit.h"
50 #include "canvas-note.h"
51 #include "canvas_patch_change.h"
54 #include "ghostregion.h"
55 #include "gui_thread.h"
57 #include "midi_channel_dialog.h"
58 #include "midi_cut_buffer.h"
59 #include "midi_list_editor.h"
60 #include "midi_region_view.h"
61 #include "midi_streamview.h"
62 #include "midi_time_axis.h"
63 #include "midi_util.h"
64 #include "note_player.h"
65 #include "public_editor.h"
66 #include "rgb_macros.h"
67 #include "selection.h"
68 #include "simpleline.h"
69 #include "streamview.h"
71 #include "mouse_cursors.h"
72 #include "patch_change_dialog.h"
73 #include "verbose_cursor.h"
77 using namespace ARDOUR;
79 using namespace Editing;
80 using namespace ArdourCanvas;
81 using Gtkmm2ext::Keyboard;
83 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
84 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
85 : RegionView (parent, tv, r, spu, basic_color)
87 , _last_channel_selection(0xFFFF)
88 , _current_range_min(0)
89 , _current_range_max(0)
90 , _model_name(string())
91 , _custom_device_mode(string())
93 , _note_group(new ArdourCanvas::Group(*group))
94 , _note_diff_command (0)
97 , _step_edit_cursor (0)
98 , _step_edit_cursor_width (1.0)
99 , _step_edit_cursor_position (0.0)
100 , _channel_selection_scoped_note (0)
101 , _temporary_note_group (0)
104 , _sort_needed (true)
105 , _optimization_iterator (_events.end())
107 , _no_sound_notes (false)
110 , _pre_enter_cursor (0)
112 _note_group->raise_to_top();
113 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
115 connect_to_diskstream ();
118 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
119 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
120 TimeAxisViewItem::Visibility visibility)
121 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
123 , _last_channel_selection(0xFFFF)
124 , _model_name(string())
125 , _custom_device_mode(string())
127 , _note_group(new ArdourCanvas::Group(*parent))
128 , _note_diff_command (0)
131 , _step_edit_cursor (0)
132 , _step_edit_cursor_width (1.0)
133 , _step_edit_cursor_position (0.0)
134 , _channel_selection_scoped_note (0)
135 , _temporary_note_group (0)
138 , _sort_needed (true)
139 , _optimization_iterator (_events.end())
141 , _no_sound_notes (false)
145 _note_group->raise_to_top();
146 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
148 connect_to_diskstream ();
151 MidiRegionView::MidiRegionView (const MidiRegionView& other)
152 : sigc::trackable(other)
155 , _last_channel_selection(0xFFFF)
156 , _model_name(string())
157 , _custom_device_mode(string())
159 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
160 , _note_diff_command (0)
163 , _step_edit_cursor (0)
164 , _step_edit_cursor_width (1.0)
165 , _step_edit_cursor_position (0.0)
166 , _channel_selection_scoped_note (0)
167 , _temporary_note_group (0)
170 , _sort_needed (true)
171 , _optimization_iterator (_events.end())
173 , _no_sound_notes (false)
180 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
181 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
186 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
187 : RegionView (other, boost::shared_ptr<Region> (region))
189 , _last_channel_selection(0xFFFF)
190 , _model_name(string())
191 , _custom_device_mode(string())
193 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
194 , _note_diff_command (0)
197 , _step_edit_cursor (0)
198 , _step_edit_cursor_width (1.0)
199 , _step_edit_cursor_position (0.0)
200 , _channel_selection_scoped_note (0)
201 , _temporary_note_group (0)
204 , _sort_needed (true)
205 , _optimization_iterator (_events.end())
207 , _no_sound_notes (false)
214 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
215 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
221 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
223 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
225 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
226 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
230 midi_region()->midi_source(0)->load_model();
233 _model = midi_region()->midi_source(0)->model();
234 _enable_display = false;
236 RegionView::init (basic_color, false);
238 compute_colors (basic_color);
240 set_height (trackview.current_height());
243 region_sync_changed ();
244 region_resized (ARDOUR::bounds_change);
247 reset_width_dependent_items (_pixel_width);
251 _enable_display = true;
254 display_model (_model);
258 group->raise_to_top();
259 group->signal_event().connect(
260 sigc::mem_fun(this, &MidiRegionView::canvas_event), false);
262 midi_view()->signal_channel_mode_changed().connect(
263 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
265 midi_view()->signal_midi_patch_settings_changed().connect(
266 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
268 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
269 ui_bind(&MidiRegionView::snap_changed, this),
272 connect_to_diskstream ();
276 MidiRegionView::connect_to_diskstream ()
278 midi_view()->midi_track()->DataRecorded.connect(
279 *this, invalidator(*this),
280 ui_bind(&MidiRegionView::data_recorded, this, _1, _2),
285 MidiRegionView::canvas_event(GdkEvent* ev)
288 case GDK_ENTER_NOTIFY:
289 case GDK_LEAVE_NOTIFY:
290 _last_event_x = ev->crossing.x;
291 _last_event_y = ev->crossing.y;
293 case GDK_MOTION_NOTIFY:
294 _last_event_x = ev->motion.x;
295 _last_event_y = ev->motion.y;
301 if (!trackview.editor().internal_editing()) {
307 return scroll (&ev->scroll);
310 return key_press (&ev->key);
312 case GDK_KEY_RELEASE:
313 return key_release (&ev->key);
315 case GDK_BUTTON_PRESS:
316 return button_press (&ev->button);
318 case GDK_2BUTTON_PRESS:
321 case GDK_BUTTON_RELEASE:
322 return button_release (&ev->button);
324 case GDK_ENTER_NOTIFY:
325 return enter_notify (&ev->crossing);
327 case GDK_LEAVE_NOTIFY:
328 return leave_notify (&ev->crossing);
330 case GDK_MOTION_NOTIFY:
331 return motion (&ev->motion);
341 MidiRegionView::remove_ghost_note ()
348 MidiRegionView::enter_notify (GdkEventCrossing* ev)
350 trackview.editor().MouseModeChanged.connect (
351 _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
354 Keyboard::magic_widget_grab_focus();
357 if (trackview.editor().current_mouse_mode() == MouseRange) {
358 create_ghost_note (ev->x, ev->y);
365 MidiRegionView::leave_notify (GdkEventCrossing*)
367 _mouse_mode_connection.disconnect ();
369 trackview.editor().verbose_cursor()->hide ();
370 remove_ghost_note ();
375 MidiRegionView::mouse_mode_changed ()
377 if (trackview.editor().current_mouse_mode() == MouseRange && trackview.editor().internal_editing()) {
378 create_ghost_note (_last_event_x, _last_event_y);
380 remove_ghost_note ();
381 trackview.editor().verbose_cursor()->hide ();
386 MidiRegionView::button_press (GdkEventButton* ev)
388 if (ev->button != 1) {
395 group->w2i (_last_x, _last_y);
397 if (_mouse_state != SelectTouchDragging) {
399 _pressed_button = ev->button;
400 _mouse_state = Pressed;
405 _pressed_button = ev->button;
411 MidiRegionView::button_release (GdkEventButton* ev)
413 double event_x, event_y;
414 framepos_t event_frame = 0;
416 if (ev->button != 1) {
423 group->w2i(event_x, event_y);
424 group->ungrab(ev->time);
426 event_frame = trackview.editor().pixel_to_frame(event_x);
428 switch (_mouse_state) {
429 case Pressed: // Clicked
431 switch (trackview.editor().current_mouse_mode()) {
437 if (Keyboard::is_insert_note_event(ev)) {
439 double event_x, event_y;
443 group->w2i(event_x, event_y);
446 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
452 create_note_at (event_x, event_y, beats, true);
460 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
466 create_note_at (event_x, event_y, beats, true);
477 case SelectRectDragging: // Select drag done
484 case AddDragging: // Add drag done
488 if (Keyboard::is_insert_note_event(ev) || trackview.editor().current_mouse_mode() == MouseRange) {
490 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
492 const double x = _drag_rect->property_x1();
493 const double length = trackview.editor().pixel_to_frame (_drag_rect->property_x2() - _drag_rect->property_x1());
495 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), true);
502 create_ghost_note (ev->x, ev->y);
512 MidiRegionView::motion (GdkEventMotion* ev)
514 double event_x, event_y;
515 framepos_t event_frame = 0;
519 group->w2i(event_x, event_y);
521 // convert event_x to global frame
522 event_frame = snap_pixel_to_frame (event_x);
524 if (!_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
525 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())
526 && _mouse_state != AddDragging) {
528 create_ghost_note (ev->x, ev->y);
529 } else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
530 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
532 update_ghost_note (ev->x, ev->y);
533 } else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange) {
538 trackview.editor().verbose_cursor()->hide ();
539 } else if (_ghost_note && trackview.editor().current_mouse_mode() == MouseRange) {
540 update_ghost_note (ev->x, ev->y);
543 /* any motion immediately hides velocity text that may have been visible */
545 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
546 (*i)->hide_velocity ();
549 switch (_mouse_state) {
550 case Pressed: // Maybe start a drag, if we've moved a bit
552 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
553 /* no appreciable movement since the button was pressed */
557 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject
558 && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
561 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
562 Gdk::Cursor(Gdk::FLEUR), ev->time);
566 _drag_start_x = event_x;
567 _drag_start_y = event_y;
569 _drag_rect = new ArdourCanvas::SimpleRect(*group);
570 _drag_rect->property_x1() = event_x;
571 _drag_rect->property_y1() = event_y;
572 _drag_rect->property_x2() = event_x;
573 _drag_rect->property_y2() = event_y;
574 _drag_rect->property_outline_what() = 0xFF;
575 _drag_rect->property_outline_color_rgba()
576 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
577 _drag_rect->property_fill_color_rgba()
578 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
580 _mouse_state = SelectRectDragging;
583 } else if (trackview.editor().internal_editing()) {
584 // Add note drag start
589 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
590 Gdk::Cursor(Gdk::FLEUR), ev->time);
594 _drag_start_x = event_x;
595 _drag_start_y = event_y;
597 _drag_rect = new ArdourCanvas::SimpleRect(*group);
598 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
600 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
601 midi_stream_view()->y_to_note(event_y));
602 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
603 _drag_rect->property_y2() = _drag_rect->property_y1()
604 + floor(midi_stream_view()->note_height());
605 _drag_rect->property_outline_what() = 0xFF;
606 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
607 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
609 _mouse_state = AddDragging;
616 trackview.editor().verbose_cursor()->hide ();
624 case SelectRectDragging: // Select drag motion
625 case AddDragging: // Add note drag motion
630 GdkModifierType state;
631 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
636 if (_mouse_state == AddDragging) {
637 event_x = trackview.editor().frame_to_pixel(event_frame);
642 if (event_x > _drag_start_x) {
643 _drag_rect->property_x2() = event_x;
646 _drag_rect->property_x1() = event_x;
650 if (_drag_rect && _mouse_state == SelectRectDragging) {
652 if (event_y > _drag_start_y) {
653 _drag_rect->property_y2() = event_y;
656 _drag_rect->property_y1() = event_y;
659 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
665 case SelectTouchDragging:
677 MidiRegionView::scroll (GdkEventScroll* ev)
679 if (_selection.empty()) {
683 trackview.editor().verbose_cursor()->hide ();
685 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
687 if (ev->direction == GDK_SCROLL_UP) {
688 change_velocities (true, fine, false);
689 } else if (ev->direction == GDK_SCROLL_DOWN) {
690 change_velocities (false, fine, false);
696 MidiRegionView::key_press (GdkEventKey* ev)
698 /* since GTK bindings are generally activated on press, and since
699 detectable auto-repeat is the name of the game and only sends
700 repeated presses, carry out key actions at key press, not release.
703 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
704 _mouse_state = SelectTouchDragging;
707 } else if (ev->keyval == GDK_Escape) {
711 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
713 bool start = (ev->keyval == GDK_comma);
714 bool end = (ev->keyval == GDK_period);
715 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
716 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
718 change_note_lengths (fine, shorter, 0.0, start, end);
722 } else if (ev->keyval == GDK_Delete) {
727 } else if (ev->keyval == GDK_Tab) {
729 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
730 goto_previous_note ();
736 } else if (ev->keyval == GDK_Up) {
738 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
739 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
741 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
742 change_velocities (true, fine, allow_smush);
744 transpose (true, fine, allow_smush);
748 } else if (ev->keyval == GDK_Down) {
750 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
751 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
753 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
754 change_velocities (false, fine, allow_smush);
756 transpose (false, fine, allow_smush);
760 } else if (ev->keyval == GDK_Left) {
765 } else if (ev->keyval == GDK_Right) {
770 } else if (ev->keyval == GDK_Control_L) {
773 } else if (ev->keyval == GDK_c) {
782 MidiRegionView::key_release (GdkEventKey* ev)
784 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
792 MidiRegionView::channel_edit ()
796 uint8_t current_channel;
798 if (_selection.empty()) {
802 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
803 Selection::iterator next = i;
805 current_channel = (*i)->note()->channel ();
808 if (current_channel != (*i)->note()->channel()) {
814 MidiChannelDialog channel_dialog (current_channel);
815 int ret = channel_dialog.run ();
818 case Gtk::RESPONSE_OK:
824 uint8_t new_channel = channel_dialog.active_channel ();
826 start_note_diff_command (_("channel edit"));
828 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
829 Selection::iterator next = i;
831 change_note_channel (*i, new_channel);
839 MidiRegionView::show_list_editor ()
842 _list_editor = new MidiListEditor (trackview.session(), midi_region());
844 _list_editor->present ();
847 /** Add a note to the model, and the view, at a canvas (click) coordinate.
848 * \param x horizontal position in pixels
849 * \param y vertical position in pixels
850 * \param length duration of the note in beats, which will be snapped to the grid
851 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
854 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
856 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
857 MidiStreamView* const view = mtv->midi_view();
859 double note = view->y_to_note(y);
862 assert(note <= 127.0);
864 // Start of note in frames relative to region start
865 framepos_t const start_frames = snap_pixel_to_frame (x);
866 assert(start_frames >= 0);
869 length = frames_to_beats(
870 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
872 assert (length != 0);
875 length = frames_to_beats (beats_to_frames (length) - 1);
878 const boost::shared_ptr<NoteType> new_note (new NoteType (mtv->get_channel_for_add (),
879 frames_to_beats(start_frames + _region->start()), length,
880 (uint8_t)note, 0x40));
882 if (_model->contains (new_note)) {
886 view->update_note_range(new_note->note());
888 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note");
890 _model->apply_command(*trackview.session(), cmd);
892 play_midi_note (new_note);
896 MidiRegionView::clear_events()
901 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
902 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
907 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
912 _patch_changes.clear();
914 _optimization_iterator = _events.end();
918 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
922 content_connection.disconnect ();
923 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
927 if (_enable_display) {
933 MidiRegionView::start_note_diff_command (string name)
935 if (!_note_diff_command) {
936 _note_diff_command = _model->new_note_diff_command (name);
941 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
943 if (_note_diff_command) {
944 _note_diff_command->add (note);
947 _marked_for_selection.insert(note);
950 _marked_for_velocity.insert(note);
955 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
957 if (_note_diff_command && ev->note()) {
958 _note_diff_command->remove(ev->note());
963 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
964 MidiModel::NoteDiffCommand::Property property,
967 if (_note_diff_command) {
968 _note_diff_command->change (ev->note(), property, val);
973 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
974 MidiModel::NoteDiffCommand::Property property,
975 Evoral::MusicalTime val)
977 if (_note_diff_command) {
978 _note_diff_command->change (ev->note(), property, val);
983 MidiRegionView::apply_diff (bool as_subcommand)
987 if (!_note_diff_command) {
991 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
992 // Mark all selected notes for selection when model reloads
993 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
994 _marked_for_selection.insert((*i)->note());
999 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1001 _model->apply_command (*trackview.session(), _note_diff_command);
1004 _note_diff_command = 0;
1005 midi_view()->midi_track()->playlist_modified();
1007 if (add_or_remove) {
1008 _marked_for_selection.clear();
1011 _marked_for_velocity.clear();
1015 MidiRegionView::abort_command()
1017 delete _note_diff_command;
1018 _note_diff_command = 0;
1023 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1025 if (_optimization_iterator != _events.end()) {
1026 ++_optimization_iterator;
1029 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1030 return *_optimization_iterator;
1033 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1034 if ((*_optimization_iterator)->note() == note) {
1035 return *_optimization_iterator;
1043 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1045 MidiModel::Notes notes;
1046 _model->get_notes (notes, op, val, chan_mask);
1048 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1049 CanvasNoteEvent* cne = find_canvas_note (*n);
1057 MidiRegionView::redisplay_model()
1059 // Don't redisplay the model if we're currently recording and displaying that
1060 if (_active_notes) {
1068 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1069 (*i)->invalidate ();
1072 MidiModel::ReadLock lock(_model->read_lock());
1074 MidiModel::Notes& notes (_model->notes());
1075 _optimization_iterator = _events.begin();
1077 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1079 boost::shared_ptr<NoteType> note (*n);
1080 CanvasNoteEvent* cne;
1083 if (note_in_region_range (note, visible)) {
1085 if ((cne = find_canvas_note (note)) != 0) {
1092 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1094 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1106 add_note (note, visible);
1111 if ((cne = find_canvas_note (note)) != 0) {
1119 /* remove note items that are no longer valid */
1121 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1122 if (!(*i)->valid ()) {
1124 i = _events.erase (i);
1130 _patch_changes.clear();
1134 display_patch_changes ();
1136 _marked_for_selection.clear ();
1137 _marked_for_velocity.clear ();
1139 /* we may have caused _events to contain things out of order (e.g. if a note
1140 moved earlier or later). we don't generally need them in time order, but
1141 make a note that a sort is required for those cases that require it.
1144 _sort_needed = true;
1148 MidiRegionView::display_patch_changes ()
1150 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1151 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1153 for (uint8_t i = 0; i < 16; ++i) {
1154 if (chn_mask & (1<<i)) {
1155 display_patch_changes_on_channel (i);
1161 MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
1163 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1165 if ((*i)->channel() != channel) {
1169 MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
1171 boost::shared_ptr<MIDI::Name::Patch> patch =
1172 MIDI::Name::MidiPatchManager::instance().find_patch(
1173 _model_name, _custom_device_mode, channel, patch_key);
1176 add_canvas_patch_change (*i, patch->name());
1179 /* program and bank numbers are zero-based: convert to one-based */
1180 snprintf (buf, 16, "%d\n%d", (*i)->program() + 1, (*i)->bank() + 1);
1181 add_canvas_patch_change (*i, buf);
1187 MidiRegionView::display_sysexes()
1189 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1190 Evoral::MusicalTime time = (*i)->time();
1195 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1196 str << int((*i)->buffer()[b]);
1197 if (b != (*i)->size() -1) {
1201 string text = str.str();
1203 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1205 double height = midi_stream_view()->contents_height();
1207 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1208 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1210 // Show unless patch change is beyond the region bounds
1211 if (time - _region->start() >= _region->length() || time < _region->start()) {
1217 _sys_exes.push_back(sysex);
1222 MidiRegionView::~MidiRegionView ()
1224 in_destructor = true;
1226 trackview.editor().verbose_cursor()->hide ();
1228 note_delete_connection.disconnect ();
1230 delete _list_editor;
1232 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1234 if (_active_notes) {
1242 delete _note_diff_command;
1243 delete _step_edit_cursor;
1244 delete _temporary_note_group;
1248 MidiRegionView::region_resized (const PropertyChange& what_changed)
1250 RegionView::region_resized(what_changed);
1252 if (what_changed.contains (ARDOUR::Properties::position)) {
1253 set_duration(_region->length(), 0);
1254 if (_enable_display) {
1261 MidiRegionView::reset_width_dependent_items (double pixel_width)
1263 RegionView::reset_width_dependent_items(pixel_width);
1264 assert(_pixel_width == pixel_width);
1266 if (_enable_display) {
1270 move_step_edit_cursor (_step_edit_cursor_position);
1271 set_step_edit_cursor_width (_step_edit_cursor_width);
1275 MidiRegionView::set_height (double height)
1277 static const double FUDGE = 2.0;
1278 const double old_height = _height;
1279 RegionView::set_height(height);
1280 _height = height - FUDGE;
1282 apply_note_range(midi_stream_view()->lowest_note(),
1283 midi_stream_view()->highest_note(),
1284 height != old_height + FUDGE);
1287 name_pixbuf->raise_to_top();
1290 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1291 (*x)->set_height (midi_stream_view()->contents_height());
1294 if (_step_edit_cursor) {
1295 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1300 /** Apply the current note range from the stream view
1301 * by repositioning/hiding notes as necessary
1304 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1306 if (!_enable_display) {
1310 if (!force && _current_range_min == min && _current_range_max == max) {
1314 _current_range_min = min;
1315 _current_range_max = max;
1317 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1318 CanvasNoteEvent* event = *i;
1319 boost::shared_ptr<NoteType> note (event->note());
1321 if (note->note() < _current_range_min ||
1322 note->note() > _current_range_max) {
1328 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1330 const double y1 = midi_stream_view()->note_to_y(note->note());
1331 const double y2 = y1 + floor(midi_stream_view()->note_height());
1333 cnote->property_y1() = y1;
1334 cnote->property_y2() = y2;
1336 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1338 const double diamond_size = update_hit (chit);
1340 chit->set_height (diamond_size);
1346 MidiRegionView::add_ghost (TimeAxisView& tv)
1350 double unit_position = _region->position () / samples_per_unit;
1351 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1352 MidiGhostRegion* ghost;
1354 if (mtv && mtv->midi_view()) {
1355 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1356 to allow having midi notes on top of note lines and waveforms.
1358 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1360 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1363 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1364 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1365 ghost->add_note(note);
1369 ghost->set_height ();
1370 ghost->set_duration (_region->length() / samples_per_unit);
1371 ghosts.push_back (ghost);
1373 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1379 /** Begin tracking note state for successive calls to add_event
1382 MidiRegionView::begin_write()
1384 assert(!_active_notes);
1385 _active_notes = new CanvasNote*[128];
1386 for (unsigned i=0; i < 128; ++i) {
1387 _active_notes[i] = 0;
1392 /** Destroy note state for add_event
1395 MidiRegionView::end_write()
1397 delete[] _active_notes;
1399 _marked_for_selection.clear();
1400 _marked_for_velocity.clear();
1404 /** Resolve an active MIDI note (while recording).
1407 MidiRegionView::resolve_note(uint8_t note, double end_time)
1409 if (midi_view()->note_mode() != Sustained) {
1413 if (_active_notes && _active_notes[note]) {
1415 const framepos_t end_time_frames = beats_to_frames(end_time);
1417 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1418 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1419 _active_notes[note] = 0;
1424 /** Extend active notes to rightmost edge of region (if length is changed)
1427 MidiRegionView::extend_active_notes()
1429 if (!_active_notes) {
1433 for (unsigned i=0; i < 128; ++i) {
1434 if (_active_notes[i]) {
1435 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1442 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1444 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1448 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1450 if (!route_ui || !route_ui->midi_track()) {
1454 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1460 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1462 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1466 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1468 if (!route_ui || !route_ui->midi_track()) {
1472 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1474 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1483 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1485 const framepos_t note_start_frames = beats_to_frames(note->time());
1487 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1488 (note_start_frames < _region->start());
1490 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1491 (note->note() <= midi_stream_view()->highest_note());
1496 /** Update a canvas note's size from its model note.
1497 * @param ev Canvas note to update.
1498 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1501 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1503 boost::shared_ptr<NoteType> note = ev->note();
1505 const framepos_t note_start_frames = beats_to_frames(note->time());
1507 /* trim note display to not overlap the end of its region */
1508 const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1510 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1511 const double y1 = midi_stream_view()->note_to_y(note->note());
1512 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1514 ev->property_x1() = x;
1515 ev->property_y1() = y1;
1517 if (note->length() > 0) {
1518 ev->property_x2() = note_endpixel;
1520 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1523 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1525 if (note->length() == 0) {
1526 if (_active_notes) {
1527 assert(note->note() < 128);
1528 // If this note is already active there's a stuck note,
1529 // finish the old note rectangle
1530 if (_active_notes[note->note()]) {
1531 CanvasNote* const old_rect = _active_notes[note->note()];
1532 boost::shared_ptr<NoteType> old_note = old_rect->note();
1533 old_rect->property_x2() = x;
1534 old_rect->property_outline_what() = (guint32) 0xF;
1536 _active_notes[note->note()] = ev;
1538 /* outline all but right edge */
1539 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1541 /* outline all edges */
1542 ev->property_outline_what() = (guint32) 0xF;
1545 if (update_ghost_regions) {
1546 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1547 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1549 gr->update_note (ev);
1556 MidiRegionView::update_hit (CanvasHit* ev)
1558 boost::shared_ptr<NoteType> note = ev->note();
1560 const framepos_t note_start_frames = beats_to_frames(note->time());
1561 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1562 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1563 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1567 return diamond_size;
1570 /** Add a MIDI note to the view (with length).
1572 * If in sustained mode, notes with length 0 will be considered active
1573 * notes, and resolve_note should be called when the corresponding note off
1574 * event arrives, to properly display the note.
1577 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1579 CanvasNoteEvent* event = 0;
1581 assert(note->time() >= 0);
1582 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1584 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1586 if (midi_view()->note_mode() == Sustained) {
1588 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1590 update_note (ev_rect);
1594 MidiGhostRegion* gr;
1596 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1597 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1598 gr->add_note(ev_rect);
1602 } else if (midi_view()->note_mode() == Percussive) {
1604 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1606 CanvasHit* ev_diamond = new CanvasHit(*this, *_note_group, diamond_size, note);
1608 update_hit (ev_diamond);
1617 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1618 note_selected(event, true);
1621 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1622 event->show_velocity();
1625 event->on_channel_selection_change(_last_channel_selection);
1626 _events.push_back(event);
1635 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1636 MidiStreamView* const view = mtv->midi_view();
1638 view->update_note_range(note->note());
1642 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1643 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1645 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1647 /* potentially extend region to hold new note */
1649 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1650 framepos_t region_end = _region->position() + _region->length() - 1;
1652 if (end_frame > region_end) {
1653 _region->set_length (end_frame - _region->position());
1656 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1657 MidiStreamView* const view = mtv->midi_view();
1659 view->update_note_range(new_note->note());
1661 _marked_for_selection.clear ();
1664 start_note_diff_command (_("step add"));
1665 note_diff_add_note (new_note, true, false);
1668 // last_step_edit_note = new_note;
1672 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1674 change_note_lengths (false, false, beats, false, true);
1678 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
1680 assert (patch->time() >= 0);
1682 const double x = trackview.editor().frame_to_pixel (beats_to_frames (patch->time()));
1684 double const height = midi_stream_view()->contents_height();
1686 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1687 new CanvasPatchChange(*this, *_note_group,
1692 _custom_device_mode,
1696 // Show unless patch change is beyond the region bounds
1697 if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1698 patch_change->hide();
1700 patch_change->show();
1703 _patch_changes.push_back (patch_change);
1707 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1709 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1710 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1714 if (i != _model->patch_changes().end()) {
1715 key.msb = (*i)->bank_msb ();
1716 key.lsb = (*i)->bank_lsb ();
1717 key.program_number = (*i)->program ();
1719 key.msb = key.lsb = key.program_number = 0;
1722 assert (key.is_sane());
1727 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1729 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1731 if (pc.patch()->program() != new_patch.program_number) {
1732 c->change_program (pc.patch (), new_patch.program_number);
1735 int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1736 if (pc.patch()->bank() != new_bank) {
1737 c->change_bank (pc.patch (), new_bank);
1740 _model->apply_command (*trackview.session(), c);
1742 _patch_changes.clear ();
1743 display_patch_changes ();
1747 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1749 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1751 if (old_change->time() != new_change.time()) {
1752 c->change_time (old_change, new_change.time());
1755 if (old_change->channel() != new_change.channel()) {
1756 c->change_channel (old_change, new_change.channel());
1759 if (old_change->program() != new_change.program()) {
1760 c->change_program (old_change, new_change.program());
1763 if (old_change->bank() != new_change.bank()) {
1764 c->change_bank (old_change, new_change.bank());
1767 _model->apply_command (*trackview.session(), c);
1769 _patch_changes.clear ();
1770 display_patch_changes ();
1773 /** Add a patch change to the region.
1774 * @param t Time in frames relative to region position
1775 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1776 * MidiTimeAxisView::get_channel_for_add())
1779 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1781 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1783 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1784 c->add (MidiModel::PatchChangePtr (
1785 new Evoral::PatchChange<Evoral::MusicalTime> (
1786 frames_to_beats (t + midi_region()->start()), mtv->get_channel_for_add(), patch.program(), patch.bank()
1790 _model->apply_command (*trackview.session(), c);
1792 _patch_changes.clear ();
1793 display_patch_changes ();
1797 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1799 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1800 c->change_time (pc.patch (), t);
1801 _model->apply_command (*trackview.session(), c);
1803 _patch_changes.clear ();
1804 display_patch_changes ();
1808 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1810 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1811 c->remove (pc->patch ());
1812 _model->apply_command (*trackview.session(), c);
1814 _patch_changes.clear ();
1815 display_patch_changes ();
1819 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1821 if (patch.patch()->program() < 127) {
1822 MIDI::Name::PatchPrimaryKey key;
1823 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1824 key.program_number++;
1825 change_patch_change (patch, key);
1830 MidiRegionView::next_patch (CanvasPatchChange& patch)
1832 if (patch.patch()->program() > 0) {
1833 MIDI::Name::PatchPrimaryKey key;
1834 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1835 key.program_number--;
1836 change_patch_change (patch, key);
1841 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1843 if (_selection.empty()) {
1847 if (_selection.erase (cne) > 0) {
1848 cerr << "Erased a CNE from selection\n";
1853 MidiRegionView::delete_selection()
1855 if (_selection.empty()) {
1859 start_note_diff_command (_("delete selection"));
1861 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1862 if ((*i)->selected()) {
1863 _note_diff_command->remove((*i)->note());
1873 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1875 start_note_diff_command (_("delete note"));
1876 _note_diff_command->remove (n);
1879 trackview.editor().verbose_cursor()->hide ();
1883 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1885 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1886 if ((*i)->selected() && (*i) != ev) {
1887 (*i)->set_selected(false);
1888 (*i)->hide_velocity();
1896 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1898 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1901 Selection::iterator tmp = i;
1904 (*i)->set_selected (false);
1905 _selection.erase (i);
1914 /* don't bother with removing this regionview from the editor selection,
1915 since we're about to add another note, and thus put/keep this
1916 regionview in the editor selection.
1919 if (!ev->selected()) {
1920 add_to_selection (ev);
1925 MidiRegionView::select_all_notes ()
1929 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1930 add_to_selection (*i);
1935 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1937 uint8_t low_note = 127;
1938 uint8_t high_note = 0;
1939 MidiModel::Notes& notes (_model->notes());
1940 _optimization_iterator = _events.begin();
1946 if (extend && _selection.empty()) {
1952 /* scan existing selection to get note range */
1954 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1955 if ((*i)->note()->note() < low_note) {
1956 low_note = (*i)->note()->note();
1958 if ((*i)->note()->note() > high_note) {
1959 high_note = (*i)->note()->note();
1963 low_note = min (low_note, notenum);
1964 high_note = max (high_note, notenum);
1967 _no_sound_notes = true;
1969 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1971 boost::shared_ptr<NoteType> note (*n);
1972 CanvasNoteEvent* cne;
1973 bool select = false;
1975 if (((1 << note->channel()) & channel_mask) != 0) {
1977 if ((note->note() >= low_note && note->note() <= high_note)) {
1980 } else if (note->note() == notenum) {
1986 if ((cne = find_canvas_note (note)) != 0) {
1987 // extend is false because we've taken care of it,
1988 // since it extends by time range, not pitch.
1989 note_selected (cne, add, false);
1993 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1997 _no_sound_notes = false;
2001 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2003 MidiModel::Notes& notes (_model->notes());
2004 _optimization_iterator = _events.begin();
2006 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2008 boost::shared_ptr<NoteType> note (*n);
2009 CanvasNoteEvent* cne;
2011 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2012 if ((cne = find_canvas_note (note)) != 0) {
2013 if (cne->selected()) {
2014 note_deselected (cne);
2016 note_selected (cne, true, false);
2024 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2027 clear_selection_except(ev);
2032 if (!ev->selected()) {
2033 add_to_selection (ev);
2037 /* find end of latest note selected, select all between that and the start of "ev" */
2039 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2040 Evoral::MusicalTime latest = 0;
2042 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2043 if ((*i)->note()->end_time() > latest) {
2044 latest = (*i)->note()->end_time();
2046 if ((*i)->note()->time() < earliest) {
2047 earliest = (*i)->note()->time();
2051 if (ev->note()->end_time() > latest) {
2052 latest = ev->note()->end_time();
2055 if (ev->note()->time() < earliest) {
2056 earliest = ev->note()->time();
2059 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2061 /* find notes entirely within OR spanning the earliest..latest range */
2063 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2064 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2065 add_to_selection (*i);
2069 /* if events were guaranteed to be time sorted, we could do this.
2070 but as of sept 10th 2009, they no longer are.
2073 if ((*i)->note()->time() > latest) {
2082 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2084 remove_from_selection (ev);
2088 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
2098 // TODO: Make this faster by storing the last updated selection rect, and only
2099 // adjusting things that are in the area that appears/disappeared.
2100 // We probably need a tree to be able to find events in O(log(n)) time.
2102 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2104 /* check if any corner of the note is inside the rect
2107 1) this is computing "touched by", not "contained by" the rect.
2108 2) this does not require that events be sorted in time.
2111 const double ix1 = (*i)->x1();
2112 const double ix2 = (*i)->x2();
2113 const double iy1 = (*i)->y1();
2114 const double iy2 = (*i)->y2();
2116 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2117 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2118 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2119 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2122 if (!(*i)->selected()) {
2123 add_to_selection (*i);
2125 } else if ((*i)->selected()) {
2126 // Not inside rectangle
2127 remove_from_selection (*i);
2133 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2135 Selection::iterator i = _selection.find (ev);
2137 if (i != _selection.end()) {
2138 _selection.erase (i);
2141 ev->set_selected (false);
2142 ev->hide_velocity ();
2144 if (_selection.empty()) {
2145 PublicEditor& editor (trackview.editor());
2146 editor.get_selection().remove (this);
2151 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2153 bool add_mrv_selection = false;
2155 if (_selection.empty()) {
2156 add_mrv_selection = true;
2159 if (_selection.insert (ev).second) {
2160 ev->set_selected (true);
2161 play_midi_note ((ev)->note());
2164 if (add_mrv_selection) {
2165 PublicEditor& editor (trackview.editor());
2166 editor.get_selection().add (this);
2171 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2173 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2174 PossibleChord to_play;
2175 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2177 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2178 if ((*i)->note()->time() < earliest) {
2179 earliest = (*i)->note()->time();
2183 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2184 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2185 to_play.push_back ((*i)->note());
2187 (*i)->move_event(dx, dy);
2190 if (dy && !_selection.empty() && !_no_sound_notes && trackview.editor().sound_notes()) {
2192 if (to_play.size() > 1) {
2194 PossibleChord shifted;
2196 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2197 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2198 moved_note->set_note (moved_note->note() + cumulative_dy);
2199 shifted.push_back (moved_note);
2202 play_midi_chord (shifted);
2204 } else if (!to_play.empty()) {
2206 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2207 moved_note->set_note (moved_note->note() + cumulative_dy);
2208 play_midi_note (moved_note);
2214 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2216 assert (!_selection.empty());
2218 uint8_t lowest_note_in_selection = 127;
2219 uint8_t highest_note_in_selection = 0;
2220 uint8_t highest_note_difference = 0;
2222 // find highest and lowest notes first
2224 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2225 uint8_t pitch = (*i)->note()->note();
2226 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2227 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2231 cerr << "dnote: " << (int) dnote << endl;
2232 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2233 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2234 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2235 << int(highest_note_in_selection) << endl;
2236 cerr << "selection size: " << _selection.size() << endl;
2237 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2240 // Make sure the note pitch does not exceed the MIDI standard range
2241 if (highest_note_in_selection + dnote > 127) {
2242 highest_note_difference = highest_note_in_selection - 127;
2245 start_note_diff_command (_("move notes"));
2247 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2249 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2255 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2257 uint8_t original_pitch = (*i)->note()->note();
2258 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2260 // keep notes in standard midi range
2261 clamp_to_0_127(new_pitch);
2263 // keep original pitch if note is dragged outside valid midi range
2264 if ((original_pitch != 0 && new_pitch == 0)
2265 || (original_pitch != 127 && new_pitch == 127)) {
2266 new_pitch = original_pitch;
2269 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2270 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2272 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2277 // care about notes being moved beyond the upper/lower bounds on the canvas
2278 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2279 highest_note_in_selection > midi_stream_view()->highest_note()) {
2280 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2285 MidiRegionView::snap_pixel_to_frame(double x)
2287 PublicEditor& editor (trackview.editor());
2288 return snap_frame_to_frame (editor.pixel_to_frame (x));
2291 /** Snap a frame offset within our region using the current snap settings.
2292 * @param x Frame offset from this region's position.
2293 * @return Snapped frame offset from this region's position.
2296 MidiRegionView::snap_frame_to_frame (frameoffset_t x)
2298 PublicEditor& editor = trackview.editor();
2300 /* x is region relative, convert it to global absolute frames */
2301 framepos_t const session_frame = x + _region->position();
2303 /* try a snap in either direction */
2304 framepos_t frame = session_frame;
2305 editor.snap_to (frame, 0);
2307 /* if we went off the beginning of the region, snap forwards */
2308 if (frame < _region->position ()) {
2309 frame = session_frame;
2310 editor.snap_to (frame, 1);
2313 /* back to region relative */
2314 return frame - _region->position();
2318 MidiRegionView::snap_to_pixel(double x)
2320 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2324 MidiRegionView::get_position_pixels()
2326 framepos_t region_frame = get_position();
2327 return trackview.editor().frame_to_pixel(region_frame);
2331 MidiRegionView::get_end_position_pixels()
2333 framepos_t frame = get_position() + get_duration ();
2334 return trackview.editor().frame_to_pixel(frame);
2338 MidiRegionView::beats_to_frames(double beats) const
2340 return _time_converter.to(beats);
2344 MidiRegionView::frames_to_beats(framepos_t frames) const
2346 return _time_converter.from(frames);
2350 MidiRegionView::begin_resizing (bool /*at_front*/)
2352 _resize_data.clear();
2354 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2355 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2357 // only insert CanvasNotes into the map
2359 NoteResizeData *resize_data = new NoteResizeData();
2360 resize_data->canvas_note = note;
2362 // create a new SimpleRect from the note which will be the resize preview
2363 SimpleRect *resize_rect = new SimpleRect(
2364 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2366 // calculate the colors: get the color settings
2367 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2368 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2371 // make the resize preview notes more transparent and bright
2372 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2374 // calculate color based on note velocity
2375 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2376 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2380 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2381 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2383 resize_data->resize_rect = resize_rect;
2384 _resize_data.push_back(resize_data);
2389 /** Update resizing notes while user drags.
2390 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2391 * @param at_front which end of the note (true == note on, false == note off)
2392 * @param delta_x change in mouse position since the start of the drag
2393 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2394 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2395 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2396 * as the \a primary note.
2399 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2401 bool cursor_set = false;
2403 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2404 SimpleRect* resize_rect = (*i)->resize_rect;
2405 CanvasNote* canvas_note = (*i)->canvas_note;
2410 current_x = canvas_note->x1() + delta_x;
2412 current_x = primary->x1() + delta_x;
2416 current_x = canvas_note->x2() + delta_x;
2418 current_x = primary->x2() + delta_x;
2423 resize_rect->property_x1() = snap_to_pixel(current_x);
2424 resize_rect->property_x2() = canvas_note->x2();
2426 resize_rect->property_x2() = snap_to_pixel(current_x);
2427 resize_rect->property_x1() = canvas_note->x1();
2433 beats = snap_pixel_to_frame (current_x);
2434 beats = frames_to_beats (beats);
2439 if (beats < canvas_note->note()->end_time()) {
2440 len = canvas_note->note()->time() - beats;
2441 len += canvas_note->note()->length();
2446 if (beats >= canvas_note->note()->time()) {
2447 len = beats - canvas_note->note()->time();
2454 snprintf (buf, sizeof (buf), "%.3g beats", len);
2455 show_verbose_cursor (buf, 0, 0);
2464 /** Finish resizing notes when the user releases the mouse button.
2465 * Parameters the same as for \a update_resizing().
2468 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2470 start_note_diff_command (_("resize notes"));
2472 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2473 CanvasNote* canvas_note = (*i)->canvas_note;
2474 SimpleRect* resize_rect = (*i)->resize_rect;
2479 current_x = canvas_note->x1() + delta_x;
2481 current_x = primary->x1() + delta_x;
2485 current_x = canvas_note->x2() + delta_x;
2487 current_x = primary->x2() + delta_x;
2491 current_x = snap_pixel_to_frame (current_x);
2492 current_x = frames_to_beats (current_x);
2494 if (at_front && current_x < canvas_note->note()->end_time()) {
2495 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2497 double len = canvas_note->note()->time() - current_x;
2498 len += canvas_note->note()->length();
2501 /* XXX convert to beats */
2502 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2507 double len = current_x - canvas_note->note()->time();
2510 /* XXX convert to beats */
2511 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2519 _resize_data.clear();
2524 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2526 uint8_t new_velocity;
2529 new_velocity = event->note()->velocity() + velocity;
2530 clamp_to_0_127(new_velocity);
2532 new_velocity = velocity;
2535 event->set_selected (event->selected()); // change color
2537 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2541 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2546 new_note = event->note()->note() + note;
2551 clamp_to_0_127 (new_note);
2552 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2556 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2558 bool change_start = false;
2559 bool change_length = false;
2560 Evoral::MusicalTime new_start = 0;
2561 Evoral::MusicalTime new_length = 0;
2563 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2565 front_delta: if positive - move the start of the note later in time (shortening it)
2566 if negative - move the start of the note earlier in time (lengthening it)
2568 end_delta: if positive - move the end of the note later in time (lengthening it)
2569 if negative - move the end of the note earlier in time (shortening it)
2573 if (front_delta < 0) {
2575 if (event->note()->time() < -front_delta) {
2578 new_start = event->note()->time() + front_delta; // moves earlier
2581 /* start moved toward zero, so move the end point out to where it used to be.
2582 Note that front_delta is negative, so this increases the length.
2585 new_length = event->note()->length() - front_delta;
2586 change_start = true;
2587 change_length = true;
2591 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2593 if (new_pos < event->note()->end_time()) {
2594 new_start = event->note()->time() + front_delta;
2595 /* start moved toward the end, so move the end point back to where it used to be */
2596 new_length = event->note()->length() - front_delta;
2597 change_start = true;
2598 change_length = true;
2605 bool can_change = true;
2606 if (end_delta < 0) {
2607 if (event->note()->length() < -end_delta) {
2613 new_length = event->note()->length() + end_delta;
2614 change_length = true;
2619 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2622 if (change_length) {
2623 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2628 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2630 uint8_t new_channel;
2634 if (event->note()->channel() < -chn) {
2637 new_channel = event->note()->channel() + chn;
2640 new_channel = event->note()->channel() + chn;
2643 new_channel = (uint8_t) chn;
2646 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2650 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2652 Evoral::MusicalTime new_time;
2656 if (event->note()->time() < -delta) {
2659 new_time = event->note()->time() + delta;
2662 new_time = event->note()->time() + delta;
2668 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2672 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2674 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2678 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2682 if (_selection.empty()) {
2697 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2698 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2704 start_note_diff_command (_("change velocities"));
2706 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2707 Selection::iterator next = i;
2709 change_note_velocity (*i, delta, true);
2715 if (!_selection.empty()) {
2717 snprintf (buf, sizeof (buf), "Vel %d",
2718 (int) (*_selection.begin())->note()->velocity());
2719 show_verbose_cursor (buf, 10, 10);
2725 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2727 if (_selection.empty()) {
2744 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2746 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2750 if ((int8_t) (*i)->note()->note() + delta > 127) {
2757 start_note_diff_command (_("transpose"));
2759 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2760 Selection::iterator next = i;
2762 change_note_note (*i, delta, true);
2770 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2776 /* grab the current grid distance */
2778 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2780 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2781 cerr << "Grid type not available as beats - TO BE FIXED\n";
2791 start_note_diff_command (_("change note lengths"));
2793 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2794 Selection::iterator next = i;
2797 /* note the negation of the delta for start */
2799 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2808 MidiRegionView::nudge_notes (bool forward)
2810 if (_selection.empty()) {
2814 /* pick a note as the point along the timeline to get the nudge distance.
2815 its not necessarily the earliest note, so we may want to pull the notes out
2816 into a vector and sort before using the first one.
2819 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2821 framepos_t distance;
2823 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2825 /* grid is off - use nudge distance */
2827 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2833 framepos_t next_pos = ref_point;
2836 if (max_framepos - 1 < next_pos) {
2840 if (next_pos == 0) {
2846 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2847 distance = ref_point - next_pos;
2850 if (distance == 0) {
2854 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2860 start_note_diff_command (_("nudge"));
2862 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2863 Selection::iterator next = i;
2865 change_note_time (*i, delta, true);
2873 MidiRegionView::change_channel(uint8_t channel)
2875 start_note_diff_command(_("change channel"));
2876 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2877 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2885 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2887 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2889 _pre_enter_cursor = editor->get_canvas_cursor ();
2891 if (_mouse_state == SelectTouchDragging) {
2892 note_selected (ev, true);
2895 show_verbose_cursor (ev->note ());
2899 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2901 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2903 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2904 (*i)->hide_velocity ();
2907 editor->verbose_cursor()->hide ();
2909 if (_pre_enter_cursor) {
2910 editor->set_canvas_cursor (_pre_enter_cursor);
2911 _pre_enter_cursor = 0;
2916 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
2919 s << ((int) ev->patch()->program() + 1) << ":" << (ev->patch()->bank() + 1);
2920 show_verbose_cursor (s.str(), 10, 20);
2924 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
2926 trackview.editor().verbose_cursor()->hide ();
2930 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
2932 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2934 if (x_fraction > 0.0 && x_fraction < 0.25) {
2935 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
2936 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
2937 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
2939 if (_pre_enter_cursor && can_set_cursor) {
2940 editor->set_canvas_cursor (_pre_enter_cursor);
2946 MidiRegionView::set_frame_color()
2950 TimeAxisViewItem::set_frame_color ();
2957 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2958 } else if (high_enough_for_name) {
2959 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2964 if (!rect_visible) {
2965 f = UINT_RGBA_CHANGE_A (f, 0);
2968 frame->property_fill_color_rgba() = f;
2972 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2976 case FilterChannels:
2977 _force_channel = -1;
2980 _force_channel = mask;
2981 mask = 0xFFFF; // Show all notes as active (below)
2984 // Update notes for selection
2985 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2986 (*i)->on_channel_selection_change(mask);
2989 _last_channel_selection = mask;
2991 _patch_changes.clear ();
2992 display_patch_changes ();
2996 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2998 _model_name = model;
2999 _custom_device_mode = custom_device_mode;
3004 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3006 if (_selection.empty()) {
3010 PublicEditor& editor (trackview.editor());
3014 /* XXX what to do ? */
3018 editor.get_cut_buffer().add (selection_as_cut_buffer());
3026 start_note_diff_command();
3028 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3035 note_diff_remove_note (*i);
3045 MidiRegionView::selection_as_cut_buffer () const
3049 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3050 NoteType* n = (*i)->note().get();
3051 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3054 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3060 /** This method handles undo */
3062 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3068 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3070 trackview.session()->begin_reversible_command (_("paste"));
3072 start_note_diff_command (_("paste"));
3074 Evoral::MusicalTime beat_delta;
3075 Evoral::MusicalTime paste_pos_beats;
3076 Evoral::MusicalTime duration;
3077 Evoral::MusicalTime end_point = 0;
3079 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3080 paste_pos_beats = frames_to_beats (pos - _region->position());
3081 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3082 paste_pos_beats = 0;
3084 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",
3085 (*mcb.notes().begin())->time(),
3086 (*mcb.notes().rbegin())->end_time(),
3087 duration, pos, _region->position(),
3088 paste_pos_beats, beat_delta));
3092 for (int n = 0; n < (int) times; ++n) {
3094 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3096 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3097 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3099 /* make all newly added notes selected */
3101 note_diff_add_note (copied_note, true);
3102 end_point = copied_note->end_time();
3105 paste_pos_beats += duration;
3108 /* if we pasted past the current end of the region, extend the region */
3110 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
3111 framepos_t region_end = _region->position() + _region->length() - 1;
3113 if (end_frame > region_end) {
3115 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3117 _region->clear_changes ();
3118 _region->set_length (end_frame);
3119 trackview.session()->add_command (new StatefulDiffCommand (_region));
3124 trackview.session()->commit_reversible_command ();
3127 struct EventNoteTimeEarlyFirstComparator {
3128 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3129 return a->note()->time() < b->note()->time();
3134 MidiRegionView::time_sort_events ()
3136 if (!_sort_needed) {
3140 EventNoteTimeEarlyFirstComparator cmp;
3143 _sort_needed = false;
3147 MidiRegionView::goto_next_note ()
3149 // framepos_t pos = -1;
3150 bool use_next = false;
3152 if (_events.back()->selected()) {
3156 time_sort_events ();
3158 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3159 if ((*i)->selected()) {
3162 } else if (use_next) {
3164 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3169 /* use the first one */
3171 unique_select (_events.front());
3176 MidiRegionView::goto_previous_note ()
3178 // framepos_t pos = -1;
3179 bool use_next = false;
3181 if (_events.front()->selected()) {
3185 time_sort_events ();
3187 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3188 if ((*i)->selected()) {
3191 } else if (use_next) {
3193 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3198 /* use the last one */
3200 unique_select (*(_events.rbegin()));
3204 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3206 bool had_selected = false;
3208 time_sort_events ();
3210 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3211 if ((*i)->selected()) {
3212 selected.insert ((*i)->note());
3213 had_selected = true;
3217 if (allow_all_if_none_selected && !had_selected) {
3218 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3219 selected.insert ((*i)->note());
3225 MidiRegionView::update_ghost_note (double x, double y)
3227 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3232 _note_group->w2i (x, y);
3233 framepos_t const f = snap_pixel_to_frame (x);
3236 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
3242 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
3244 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
3245 _ghost_note->note()->set_length (length);
3246 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3247 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3249 /* the ghost note does not appear in ghost regions, so pass false in here */
3250 update_note (_ghost_note, false);
3252 show_verbose_cursor (_ghost_note->note ());
3256 MidiRegionView::create_ghost_note (double x, double y)
3261 boost::shared_ptr<NoteType> g (new NoteType);
3262 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3263 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3264 update_ghost_note (x, y);
3265 _ghost_note->show ();
3270 show_verbose_cursor (_ghost_note->note ());
3274 MidiRegionView::snap_changed ()
3280 create_ghost_note (_last_ghost_x, _last_ghost_y);
3284 MidiRegionView::drop_down_keys ()
3286 _mouse_state = None;
3290 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3292 double note = midi_stream_view()->y_to_note(y);
3294 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3296 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3298 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3299 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3300 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3301 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3306 bool add_mrv_selection = false;
3308 if (_selection.empty()) {
3309 add_mrv_selection = true;
3312 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3313 if (_selection.insert (*i).second) {
3314 (*i)->set_selected (true);
3318 if (add_mrv_selection) {
3319 PublicEditor& editor (trackview.editor());
3320 editor.get_selection().add (this);
3325 MidiRegionView::color_handler ()
3327 RegionView::color_handler ();
3329 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3330 (*i)->set_selected ((*i)->selected()); // will change color
3333 /* XXX probably more to do here */
3337 MidiRegionView::enable_display (bool yn)
3339 RegionView::enable_display (yn);
3346 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3348 if (_step_edit_cursor == 0) {
3349 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3351 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3352 _step_edit_cursor->property_y1() = 0;
3353 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3354 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3355 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3358 move_step_edit_cursor (pos);
3359 _step_edit_cursor->show ();
3363 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3365 _step_edit_cursor_position = pos;
3367 if (_step_edit_cursor) {
3368 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3369 _step_edit_cursor->property_x1() = pixel;
3370 set_step_edit_cursor_width (_step_edit_cursor_width);
3375 MidiRegionView::hide_step_edit_cursor ()
3377 if (_step_edit_cursor) {
3378 _step_edit_cursor->hide ();
3383 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3385 _step_edit_cursor_width = beats;
3387 if (_step_edit_cursor) {
3388 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
3392 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3393 * @param buf Data that has been recorded.
3394 * @param w Source that this data will end up in.
3397 MidiRegionView::data_recorded (boost::shared_ptr<MidiBuffer> buf, boost::weak_ptr<MidiSource> w)
3399 if (!_active_notes) {
3400 /* we aren't actively being recorded to */
3404 boost::shared_ptr<MidiSource> src = w.lock ();
3405 if (!src || src != midi_region()->midi_source()) {
3406 /* recorded data was not destined for our source */
3410 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3411 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3413 framepos_t back = max_framepos;
3415 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3416 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3417 assert (ev.buffer ());
3419 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3421 if (ev.type() == MIDI_CMD_NOTE_ON) {
3423 boost::shared_ptr<NoteType> note (
3424 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3427 add_note (note, true);
3429 /* fix up our note range */
3430 if (ev.note() < _current_range_min) {
3431 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3432 } else if (ev.note() > _current_range_max) {
3433 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3436 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3437 resolve_note (ev.note (), time_beats);
3443 midi_stream_view()->check_record_layers (region(), back);
3447 MidiRegionView::trim_front_starting ()
3449 /* Reparent the note group to the region view's parent, so that it doesn't change
3450 when the region view is trimmed.
3452 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3453 _temporary_note_group->move (group->property_x(), group->property_y());
3454 _note_group->reparent (*_temporary_note_group);
3458 MidiRegionView::trim_front_ending ()
3460 _note_group->reparent (*group);
3461 delete _temporary_note_group;
3462 _temporary_note_group = 0;
3464 if (_region->start() < 0) {
3465 /* Trim drag made start time -ve; fix this */
3466 midi_region()->fix_negative_start ();
3471 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3473 PatchChangeDialog d (&_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3474 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3478 change_patch_change (pc->patch(), d.patch ());
3483 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3486 snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3487 Evoral::midi_note_name (n->note()).c_str(),
3489 (int) n->channel() + 1,
3490 (int) n->velocity());
3492 show_verbose_cursor (buf, 10, 20);
3496 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3500 trackview.editor().get_pointer_position (wx, wy);
3505 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3507 double x1, y1, x2, y2;
3508 trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3510 if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3511 wy -= (y2 - y1) + 2 * yoffset;
3514 trackview.editor().verbose_cursor()->set (text, wx, wy);
3515 trackview.editor().verbose_cursor()->show ();