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;
415 if (ev->button != 1) {
422 group->w2i(event_x, event_y);
423 group->ungrab(ev->time);
425 switch (_mouse_state) {
426 case Pressed: // Clicked
428 switch (trackview.editor().current_mouse_mode()) {
434 if (Keyboard::is_insert_note_event(ev)) {
436 double event_x, event_y;
440 group->w2i(event_x, event_y);
443 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
449 create_note_at (event_x, event_y, beats, true);
457 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
463 create_note_at (event_x, event_y, beats, true);
474 case SelectRectDragging: // Select drag done
481 case AddDragging: // Add drag done
485 if (Keyboard::is_insert_note_event(ev) || trackview.editor().current_mouse_mode() == MouseRange) {
487 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
489 const double x = _drag_rect->property_x1();
490 const double length = trackview.editor().pixel_to_frame (_drag_rect->property_x2() - _drag_rect->property_x1());
492 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), true);
499 create_ghost_note (ev->x, ev->y);
509 MidiRegionView::motion (GdkEventMotion* ev)
511 double event_x, event_y;
512 framepos_t event_frame = 0;
516 group->w2i(event_x, event_y);
518 // convert event_x to global frame
519 event_frame = snap_pixel_to_frame (event_x);
521 if (!_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
522 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())
523 && _mouse_state != AddDragging) {
525 create_ghost_note (ev->x, ev->y);
526 } else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange
527 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
529 update_ghost_note (ev->x, ev->y);
530 } else if (_ghost_note && trackview.editor().current_mouse_mode() != MouseRange) {
535 trackview.editor().verbose_cursor()->hide ();
536 } else if (_ghost_note && trackview.editor().current_mouse_mode() == MouseRange) {
537 update_ghost_note (ev->x, ev->y);
540 /* any motion immediately hides velocity text that may have been visible */
542 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
543 (*i)->hide_velocity ();
546 switch (_mouse_state) {
547 case Pressed: // Maybe start a drag, if we've moved a bit
549 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
550 /* no appreciable movement since the button was pressed */
554 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject
555 && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
558 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
559 Gdk::Cursor(Gdk::FLEUR), ev->time);
563 _drag_start_x = event_x;
564 _drag_start_y = event_y;
566 _drag_rect = new ArdourCanvas::SimpleRect(*group);
567 _drag_rect->property_x1() = event_x;
568 _drag_rect->property_y1() = event_y;
569 _drag_rect->property_x2() = event_x;
570 _drag_rect->property_y2() = event_y;
571 _drag_rect->property_outline_what() = 0xFF;
572 _drag_rect->property_outline_color_rgba()
573 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
574 _drag_rect->property_fill_color_rgba()
575 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
577 _mouse_state = SelectRectDragging;
580 } else if (trackview.editor().internal_editing()) {
581 // Add note drag start
586 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
587 Gdk::Cursor(Gdk::FLEUR), ev->time);
591 _drag_start_x = event_x;
592 _drag_start_y = event_y;
594 _drag_rect = new ArdourCanvas::SimpleRect(*group);
595 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
597 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
598 midi_stream_view()->y_to_note(event_y));
599 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
600 _drag_rect->property_y2() = _drag_rect->property_y1()
601 + floor(midi_stream_view()->note_height());
602 _drag_rect->property_outline_what() = 0xFF;
603 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
604 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
606 _mouse_state = AddDragging;
613 trackview.editor().verbose_cursor()->hide ();
621 case SelectRectDragging: // Select drag motion
622 case AddDragging: // Add note drag motion
627 GdkModifierType state;
628 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
633 if (_mouse_state == AddDragging) {
634 event_x = trackview.editor().frame_to_pixel(event_frame);
639 if (event_x > _drag_start_x) {
640 _drag_rect->property_x2() = event_x;
643 _drag_rect->property_x1() = event_x;
647 if (_drag_rect && _mouse_state == SelectRectDragging) {
649 if (event_y > _drag_start_y) {
650 _drag_rect->property_y2() = event_y;
653 _drag_rect->property_y1() = event_y;
656 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
662 case SelectTouchDragging:
674 MidiRegionView::scroll (GdkEventScroll* ev)
676 if (_selection.empty()) {
680 trackview.editor().verbose_cursor()->hide ();
682 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
684 if (ev->direction == GDK_SCROLL_UP) {
685 change_velocities (true, fine, false);
686 } else if (ev->direction == GDK_SCROLL_DOWN) {
687 change_velocities (false, fine, false);
693 MidiRegionView::key_press (GdkEventKey* ev)
695 /* since GTK bindings are generally activated on press, and since
696 detectable auto-repeat is the name of the game and only sends
697 repeated presses, carry out key actions at key press, not release.
700 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
701 _mouse_state = SelectTouchDragging;
704 } else if (ev->keyval == GDK_Escape) {
708 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
710 bool start = (ev->keyval == GDK_comma);
711 bool end = (ev->keyval == GDK_period);
712 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
713 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
715 change_note_lengths (fine, shorter, 0.0, start, end);
719 } else if (ev->keyval == GDK_Delete) {
724 } else if (ev->keyval == GDK_Tab) {
726 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
727 goto_previous_note ();
733 } else if (ev->keyval == GDK_Up) {
735 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
736 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
738 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
739 change_velocities (true, fine, allow_smush);
741 transpose (true, fine, allow_smush);
745 } else if (ev->keyval == GDK_Down) {
747 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
748 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
750 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
751 change_velocities (false, fine, allow_smush);
753 transpose (false, fine, allow_smush);
757 } else if (ev->keyval == GDK_Left) {
762 } else if (ev->keyval == GDK_Right) {
767 } else if (ev->keyval == GDK_Control_L) {
770 } else if (ev->keyval == GDK_c) {
779 MidiRegionView::key_release (GdkEventKey* ev)
781 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
789 MidiRegionView::channel_edit ()
792 uint8_t current_channel;
794 if (_selection.empty()) {
798 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
800 current_channel = (*i)->note()->channel ();
805 MidiChannelDialog channel_dialog (current_channel);
806 int ret = channel_dialog.run ();
809 case Gtk::RESPONSE_OK:
815 uint8_t new_channel = channel_dialog.active_channel ();
817 start_note_diff_command (_("channel edit"));
819 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
820 Selection::iterator next = i;
822 change_note_channel (*i, new_channel);
830 MidiRegionView::show_list_editor ()
833 _list_editor = new MidiListEditor (trackview.session(), midi_region());
835 _list_editor->present ();
838 /** Add a note to the model, and the view, at a canvas (click) coordinate.
839 * \param x horizontal position in pixels
840 * \param y vertical position in pixels
841 * \param length duration of the note in beats, which will be snapped to the grid
842 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
845 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
847 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
848 MidiStreamView* const view = mtv->midi_view();
850 double note = view->y_to_note(y);
853 assert(note <= 127.0);
855 // Start of note in frames relative to region start
856 framepos_t const start_frames = snap_pixel_to_frame (x);
857 assert(start_frames >= 0);
860 length = frames_to_beats(
861 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
863 assert (length != 0);
866 length = frames_to_beats (beats_to_frames (length) - 1);
869 const boost::shared_ptr<NoteType> new_note (new NoteType (mtv->get_channel_for_add (),
870 frames_to_beats(start_frames + _region->start()), length,
871 (uint8_t)note, 0x40));
873 if (_model->contains (new_note)) {
877 view->update_note_range(new_note->note());
879 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note");
881 _model->apply_command(*trackview.session(), cmd);
883 play_midi_note (new_note);
887 MidiRegionView::clear_events()
892 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
893 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
898 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
903 _patch_changes.clear();
905 _optimization_iterator = _events.end();
909 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
913 content_connection.disconnect ();
914 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
918 if (_enable_display) {
924 MidiRegionView::start_note_diff_command (string name)
926 if (!_note_diff_command) {
927 _note_diff_command = _model->new_note_diff_command (name);
932 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
934 if (_note_diff_command) {
935 _note_diff_command->add (note);
938 _marked_for_selection.insert(note);
941 _marked_for_velocity.insert(note);
946 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
948 if (_note_diff_command && ev->note()) {
949 _note_diff_command->remove(ev->note());
954 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
955 MidiModel::NoteDiffCommand::Property property,
958 if (_note_diff_command) {
959 _note_diff_command->change (ev->note(), property, val);
964 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
965 MidiModel::NoteDiffCommand::Property property,
966 Evoral::MusicalTime val)
968 if (_note_diff_command) {
969 _note_diff_command->change (ev->note(), property, val);
974 MidiRegionView::apply_diff (bool as_subcommand)
978 if (!_note_diff_command) {
982 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
983 // Mark all selected notes for selection when model reloads
984 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
985 _marked_for_selection.insert((*i)->note());
990 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
992 _model->apply_command (*trackview.session(), _note_diff_command);
995 _note_diff_command = 0;
996 midi_view()->midi_track()->playlist_modified();
999 _marked_for_selection.clear();
1002 _marked_for_velocity.clear();
1006 MidiRegionView::abort_command()
1008 delete _note_diff_command;
1009 _note_diff_command = 0;
1014 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1016 if (_optimization_iterator != _events.end()) {
1017 ++_optimization_iterator;
1020 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1021 return *_optimization_iterator;
1024 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1025 if ((*_optimization_iterator)->note() == note) {
1026 return *_optimization_iterator;
1034 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1036 MidiModel::Notes notes;
1037 _model->get_notes (notes, op, val, chan_mask);
1039 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1040 CanvasNoteEvent* cne = find_canvas_note (*n);
1048 MidiRegionView::redisplay_model()
1050 // Don't redisplay the model if we're currently recording and displaying that
1051 if (_active_notes) {
1059 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1060 (*i)->invalidate ();
1063 MidiModel::ReadLock lock(_model->read_lock());
1065 MidiModel::Notes& notes (_model->notes());
1066 _optimization_iterator = _events.begin();
1068 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1070 boost::shared_ptr<NoteType> note (*n);
1071 CanvasNoteEvent* cne;
1074 if (note_in_region_range (note, visible)) {
1076 if ((cne = find_canvas_note (note)) != 0) {
1083 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1085 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1097 add_note (note, visible);
1102 if ((cne = find_canvas_note (note)) != 0) {
1110 /* remove note items that are no longer valid */
1112 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1113 if (!(*i)->valid ()) {
1115 i = _events.erase (i);
1121 _patch_changes.clear();
1125 display_patch_changes ();
1127 _marked_for_selection.clear ();
1128 _marked_for_velocity.clear ();
1130 /* we may have caused _events to contain things out of order (e.g. if a note
1131 moved earlier or later). we don't generally need them in time order, but
1132 make a note that a sort is required for those cases that require it.
1135 _sort_needed = true;
1139 MidiRegionView::display_patch_changes ()
1141 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1142 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1144 for (uint8_t i = 0; i < 16; ++i) {
1145 if (chn_mask & (1<<i)) {
1146 display_patch_changes_on_channel (i);
1152 MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
1154 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1156 if ((*i)->channel() != channel) {
1160 MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
1162 boost::shared_ptr<MIDI::Name::Patch> patch =
1163 MIDI::Name::MidiPatchManager::instance().find_patch(
1164 _model_name, _custom_device_mode, channel, patch_key);
1167 add_canvas_patch_change (*i, patch->name());
1170 /* program and bank numbers are zero-based: convert to one-based */
1171 snprintf (buf, 16, "%d\n%d", (*i)->program() + 1, (*i)->bank() + 1);
1172 add_canvas_patch_change (*i, buf);
1178 MidiRegionView::display_sysexes()
1180 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1181 Evoral::MusicalTime time = (*i)->time();
1186 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1187 str << int((*i)->buffer()[b]);
1188 if (b != (*i)->size() -1) {
1192 string text = str.str();
1194 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1196 double height = midi_stream_view()->contents_height();
1198 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1199 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1201 // Show unless patch change is beyond the region bounds
1202 if (time - _region->start() >= _region->length() || time < _region->start()) {
1208 _sys_exes.push_back(sysex);
1213 MidiRegionView::~MidiRegionView ()
1215 in_destructor = true;
1217 trackview.editor().verbose_cursor()->hide ();
1219 note_delete_connection.disconnect ();
1221 delete _list_editor;
1223 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1225 if (_active_notes) {
1233 delete _note_diff_command;
1234 delete _step_edit_cursor;
1235 delete _temporary_note_group;
1239 MidiRegionView::region_resized (const PropertyChange& what_changed)
1241 RegionView::region_resized(what_changed);
1243 if (what_changed.contains (ARDOUR::Properties::position)) {
1244 set_duration(_region->length(), 0);
1245 if (_enable_display) {
1252 MidiRegionView::reset_width_dependent_items (double pixel_width)
1254 RegionView::reset_width_dependent_items(pixel_width);
1255 assert(_pixel_width == pixel_width);
1257 if (_enable_display) {
1261 move_step_edit_cursor (_step_edit_cursor_position);
1262 set_step_edit_cursor_width (_step_edit_cursor_width);
1266 MidiRegionView::set_height (double height)
1268 static const double FUDGE = 2.0;
1269 const double old_height = _height;
1270 RegionView::set_height(height);
1271 _height = height - FUDGE;
1273 apply_note_range(midi_stream_view()->lowest_note(),
1274 midi_stream_view()->highest_note(),
1275 height != old_height + FUDGE);
1278 name_pixbuf->raise_to_top();
1281 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1282 (*x)->set_height (midi_stream_view()->contents_height());
1285 if (_step_edit_cursor) {
1286 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1291 /** Apply the current note range from the stream view
1292 * by repositioning/hiding notes as necessary
1295 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1297 if (!_enable_display) {
1301 if (!force && _current_range_min == min && _current_range_max == max) {
1305 _current_range_min = min;
1306 _current_range_max = max;
1308 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1309 CanvasNoteEvent* event = *i;
1310 boost::shared_ptr<NoteType> note (event->note());
1312 if (note->note() < _current_range_min ||
1313 note->note() > _current_range_max) {
1319 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1321 const double y1 = midi_stream_view()->note_to_y(note->note());
1322 const double y2 = y1 + floor(midi_stream_view()->note_height());
1324 cnote->property_y1() = y1;
1325 cnote->property_y2() = y2;
1327 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1329 const double diamond_size = update_hit (chit);
1331 chit->set_height (diamond_size);
1337 MidiRegionView::add_ghost (TimeAxisView& tv)
1341 double unit_position = _region->position () / samples_per_unit;
1342 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1343 MidiGhostRegion* ghost;
1345 if (mtv && mtv->midi_view()) {
1346 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1347 to allow having midi notes on top of note lines and waveforms.
1349 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1351 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1354 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1355 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1356 ghost->add_note(note);
1360 ghost->set_height ();
1361 ghost->set_duration (_region->length() / samples_per_unit);
1362 ghosts.push_back (ghost);
1364 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1370 /** Begin tracking note state for successive calls to add_event
1373 MidiRegionView::begin_write()
1375 assert(!_active_notes);
1376 _active_notes = new CanvasNote*[128];
1377 for (unsigned i=0; i < 128; ++i) {
1378 _active_notes[i] = 0;
1383 /** Destroy note state for add_event
1386 MidiRegionView::end_write()
1388 delete[] _active_notes;
1390 _marked_for_selection.clear();
1391 _marked_for_velocity.clear();
1395 /** Resolve an active MIDI note (while recording).
1398 MidiRegionView::resolve_note(uint8_t note, double end_time)
1400 if (midi_view()->note_mode() != Sustained) {
1404 if (_active_notes && _active_notes[note]) {
1406 const framepos_t end_time_frames = beats_to_frames(end_time);
1408 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1409 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1410 _active_notes[note] = 0;
1415 /** Extend active notes to rightmost edge of region (if length is changed)
1418 MidiRegionView::extend_active_notes()
1420 if (!_active_notes) {
1424 for (unsigned i=0; i < 128; ++i) {
1425 if (_active_notes[i]) {
1426 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1433 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1435 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1439 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1441 if (!route_ui || !route_ui->midi_track()) {
1445 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1451 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1453 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1457 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1459 if (!route_ui || !route_ui->midi_track()) {
1463 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1465 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1474 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1476 const framepos_t note_start_frames = beats_to_frames(note->time());
1478 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1479 (note_start_frames < _region->start());
1481 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1482 (note->note() <= midi_stream_view()->highest_note());
1487 /** Update a canvas note's size from its model note.
1488 * @param ev Canvas note to update.
1489 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1492 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1494 boost::shared_ptr<NoteType> note = ev->note();
1496 const framepos_t note_start_frames = beats_to_frames(note->time());
1498 /* trim note display to not overlap the end of its region */
1499 const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1501 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1502 const double y1 = midi_stream_view()->note_to_y(note->note());
1503 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1505 ev->property_x1() = x;
1506 ev->property_y1() = y1;
1508 if (note->length() > 0) {
1509 ev->property_x2() = note_endpixel;
1511 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1514 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1516 if (note->length() == 0) {
1517 if (_active_notes) {
1518 assert(note->note() < 128);
1519 // If this note is already active there's a stuck note,
1520 // finish the old note rectangle
1521 if (_active_notes[note->note()]) {
1522 CanvasNote* const old_rect = _active_notes[note->note()];
1523 boost::shared_ptr<NoteType> old_note = old_rect->note();
1524 old_rect->property_x2() = x;
1525 old_rect->property_outline_what() = (guint32) 0xF;
1527 _active_notes[note->note()] = ev;
1529 /* outline all but right edge */
1530 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1532 /* outline all edges */
1533 ev->property_outline_what() = (guint32) 0xF;
1536 if (update_ghost_regions) {
1537 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1538 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1540 gr->update_note (ev);
1547 MidiRegionView::update_hit (CanvasHit* ev)
1549 boost::shared_ptr<NoteType> note = ev->note();
1551 const framepos_t note_start_frames = beats_to_frames(note->time());
1552 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1553 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1554 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1558 return diamond_size;
1561 /** Add a MIDI note to the view (with length).
1563 * If in sustained mode, notes with length 0 will be considered active
1564 * notes, and resolve_note should be called when the corresponding note off
1565 * event arrives, to properly display the note.
1568 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1570 CanvasNoteEvent* event = 0;
1572 assert(note->time() >= 0);
1573 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1575 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1577 if (midi_view()->note_mode() == Sustained) {
1579 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1581 update_note (ev_rect);
1585 MidiGhostRegion* gr;
1587 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1588 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1589 gr->add_note(ev_rect);
1593 } else if (midi_view()->note_mode() == Percussive) {
1595 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1597 CanvasHit* ev_diamond = new CanvasHit(*this, *_note_group, diamond_size, note);
1599 update_hit (ev_diamond);
1608 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1609 note_selected(event, true);
1612 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1613 event->show_velocity();
1616 event->on_channel_selection_change(_last_channel_selection);
1617 _events.push_back(event);
1626 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1627 MidiStreamView* const view = mtv->midi_view();
1629 view->update_note_range(note->note());
1633 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1634 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1636 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1638 /* potentially extend region to hold new note */
1640 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1641 framepos_t region_end = _region->position() + _region->length() - 1;
1643 if (end_frame > region_end) {
1644 _region->set_length (end_frame - _region->position());
1647 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1648 MidiStreamView* const view = mtv->midi_view();
1650 view->update_note_range(new_note->note());
1652 _marked_for_selection.clear ();
1655 start_note_diff_command (_("step add"));
1656 note_diff_add_note (new_note, true, false);
1659 // last_step_edit_note = new_note;
1663 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1665 change_note_lengths (false, false, beats, false, true);
1669 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
1671 assert (patch->time() >= 0);
1673 const double x = trackview.editor().frame_to_pixel (beats_to_frames (patch->time()));
1675 double const height = midi_stream_view()->contents_height();
1677 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1678 new CanvasPatchChange(*this, *_note_group,
1683 _custom_device_mode,
1687 // Show unless patch change is beyond the region bounds
1688 if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1689 patch_change->hide();
1691 patch_change->show();
1694 _patch_changes.push_back (patch_change);
1698 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1700 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1701 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1705 if (i != _model->patch_changes().end()) {
1706 key.msb = (*i)->bank_msb ();
1707 key.lsb = (*i)->bank_lsb ();
1708 key.program_number = (*i)->program ();
1710 key.msb = key.lsb = key.program_number = 0;
1713 assert (key.is_sane());
1718 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1720 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1722 if (pc.patch()->program() != new_patch.program_number) {
1723 c->change_program (pc.patch (), new_patch.program_number);
1726 int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1727 if (pc.patch()->bank() != new_bank) {
1728 c->change_bank (pc.patch (), new_bank);
1731 _model->apply_command (*trackview.session(), c);
1733 _patch_changes.clear ();
1734 display_patch_changes ();
1738 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1740 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1742 if (old_change->time() != new_change.time()) {
1743 c->change_time (old_change, new_change.time());
1746 if (old_change->channel() != new_change.channel()) {
1747 c->change_channel (old_change, new_change.channel());
1750 if (old_change->program() != new_change.program()) {
1751 c->change_program (old_change, new_change.program());
1754 if (old_change->bank() != new_change.bank()) {
1755 c->change_bank (old_change, new_change.bank());
1758 _model->apply_command (*trackview.session(), c);
1760 _patch_changes.clear ();
1761 display_patch_changes ();
1764 /** Add a patch change to the region.
1765 * @param t Time in frames relative to region position
1766 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1767 * MidiTimeAxisView::get_channel_for_add())
1770 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1772 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1774 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1775 c->add (MidiModel::PatchChangePtr (
1776 new Evoral::PatchChange<Evoral::MusicalTime> (
1777 frames_to_beats (t + midi_region()->start()), mtv->get_channel_for_add(), patch.program(), patch.bank()
1781 _model->apply_command (*trackview.session(), c);
1783 _patch_changes.clear ();
1784 display_patch_changes ();
1788 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1790 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1791 c->change_time (pc.patch (), t);
1792 _model->apply_command (*trackview.session(), c);
1794 _patch_changes.clear ();
1795 display_patch_changes ();
1799 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1801 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1802 c->remove (pc->patch ());
1803 _model->apply_command (*trackview.session(), c);
1805 _patch_changes.clear ();
1806 display_patch_changes ();
1810 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1812 if (patch.patch()->program() < 127) {
1813 MIDI::Name::PatchPrimaryKey key;
1814 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1815 key.program_number++;
1816 change_patch_change (patch, key);
1821 MidiRegionView::next_patch (CanvasPatchChange& patch)
1823 if (patch.patch()->program() > 0) {
1824 MIDI::Name::PatchPrimaryKey key;
1825 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1826 key.program_number--;
1827 change_patch_change (patch, key);
1832 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1834 if (_selection.empty()) {
1838 _selection.erase (cne);
1842 MidiRegionView::delete_selection()
1844 if (_selection.empty()) {
1848 start_note_diff_command (_("delete selection"));
1850 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1851 if ((*i)->selected()) {
1852 _note_diff_command->remove((*i)->note());
1862 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1864 start_note_diff_command (_("delete note"));
1865 _note_diff_command->remove (n);
1868 trackview.editor().verbose_cursor()->hide ();
1872 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1874 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1875 if ((*i)->selected() && (*i) != ev) {
1876 (*i)->set_selected(false);
1877 (*i)->hide_velocity();
1885 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1887 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1890 Selection::iterator tmp = i;
1893 (*i)->set_selected (false);
1894 _selection.erase (i);
1903 /* don't bother with removing this regionview from the editor selection,
1904 since we're about to add another note, and thus put/keep this
1905 regionview in the editor selection.
1908 if (!ev->selected()) {
1909 add_to_selection (ev);
1914 MidiRegionView::select_all_notes ()
1918 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1919 add_to_selection (*i);
1924 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1926 uint8_t low_note = 127;
1927 uint8_t high_note = 0;
1928 MidiModel::Notes& notes (_model->notes());
1929 _optimization_iterator = _events.begin();
1935 if (extend && _selection.empty()) {
1941 /* scan existing selection to get note range */
1943 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1944 if ((*i)->note()->note() < low_note) {
1945 low_note = (*i)->note()->note();
1947 if ((*i)->note()->note() > high_note) {
1948 high_note = (*i)->note()->note();
1952 low_note = min (low_note, notenum);
1953 high_note = max (high_note, notenum);
1956 _no_sound_notes = true;
1958 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1960 boost::shared_ptr<NoteType> note (*n);
1961 CanvasNoteEvent* cne;
1962 bool select = false;
1964 if (((1 << note->channel()) & channel_mask) != 0) {
1966 if ((note->note() >= low_note && note->note() <= high_note)) {
1969 } else if (note->note() == notenum) {
1975 if ((cne = find_canvas_note (note)) != 0) {
1976 // extend is false because we've taken care of it,
1977 // since it extends by time range, not pitch.
1978 note_selected (cne, add, false);
1982 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1986 _no_sound_notes = false;
1990 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1992 MidiModel::Notes& notes (_model->notes());
1993 _optimization_iterator = _events.begin();
1995 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1997 boost::shared_ptr<NoteType> note (*n);
1998 CanvasNoteEvent* cne;
2000 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2001 if ((cne = find_canvas_note (note)) != 0) {
2002 if (cne->selected()) {
2003 note_deselected (cne);
2005 note_selected (cne, true, false);
2013 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2016 clear_selection_except(ev);
2021 if (!ev->selected()) {
2022 add_to_selection (ev);
2026 /* find end of latest note selected, select all between that and the start of "ev" */
2028 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2029 Evoral::MusicalTime latest = 0;
2031 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2032 if ((*i)->note()->end_time() > latest) {
2033 latest = (*i)->note()->end_time();
2035 if ((*i)->note()->time() < earliest) {
2036 earliest = (*i)->note()->time();
2040 if (ev->note()->end_time() > latest) {
2041 latest = ev->note()->end_time();
2044 if (ev->note()->time() < earliest) {
2045 earliest = ev->note()->time();
2048 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2050 /* find notes entirely within OR spanning the earliest..latest range */
2052 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2053 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2054 add_to_selection (*i);
2058 /* if events were guaranteed to be time sorted, we could do this.
2059 but as of sept 10th 2009, they no longer are.
2062 if ((*i)->note()->time() > latest) {
2071 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2073 remove_from_selection (ev);
2077 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
2087 // TODO: Make this faster by storing the last updated selection rect, and only
2088 // adjusting things that are in the area that appears/disappeared.
2089 // We probably need a tree to be able to find events in O(log(n)) time.
2091 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2093 /* check if any corner of the note is inside the rect
2096 1) this is computing "touched by", not "contained by" the rect.
2097 2) this does not require that events be sorted in time.
2100 const double ix1 = (*i)->x1();
2101 const double ix2 = (*i)->x2();
2102 const double iy1 = (*i)->y1();
2103 const double iy2 = (*i)->y2();
2105 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2106 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2107 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2108 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2111 if (!(*i)->selected()) {
2112 add_to_selection (*i);
2114 } else if ((*i)->selected()) {
2115 // Not inside rectangle
2116 remove_from_selection (*i);
2122 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2124 Selection::iterator i = _selection.find (ev);
2126 if (i != _selection.end()) {
2127 _selection.erase (i);
2130 ev->set_selected (false);
2131 ev->hide_velocity ();
2133 if (_selection.empty()) {
2134 PublicEditor& editor (trackview.editor());
2135 editor.get_selection().remove (this);
2140 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2142 bool add_mrv_selection = false;
2144 if (_selection.empty()) {
2145 add_mrv_selection = true;
2148 if (_selection.insert (ev).second) {
2149 ev->set_selected (true);
2150 play_midi_note ((ev)->note());
2153 if (add_mrv_selection) {
2154 PublicEditor& editor (trackview.editor());
2155 editor.get_selection().add (this);
2160 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2162 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2163 PossibleChord to_play;
2164 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2166 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2167 if ((*i)->note()->time() < earliest) {
2168 earliest = (*i)->note()->time();
2172 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2173 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2174 to_play.push_back ((*i)->note());
2176 (*i)->move_event(dx, dy);
2179 if (dy && !_selection.empty() && !_no_sound_notes && trackview.editor().sound_notes()) {
2181 if (to_play.size() > 1) {
2183 PossibleChord shifted;
2185 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2186 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2187 moved_note->set_note (moved_note->note() + cumulative_dy);
2188 shifted.push_back (moved_note);
2191 play_midi_chord (shifted);
2193 } else if (!to_play.empty()) {
2195 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2196 moved_note->set_note (moved_note->note() + cumulative_dy);
2197 play_midi_note (moved_note);
2203 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2205 assert (!_selection.empty());
2207 uint8_t lowest_note_in_selection = 127;
2208 uint8_t highest_note_in_selection = 0;
2209 uint8_t highest_note_difference = 0;
2211 // find highest and lowest notes first
2213 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2214 uint8_t pitch = (*i)->note()->note();
2215 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2216 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2220 cerr << "dnote: " << (int) dnote << endl;
2221 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2222 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2223 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2224 << int(highest_note_in_selection) << endl;
2225 cerr << "selection size: " << _selection.size() << endl;
2226 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2229 // Make sure the note pitch does not exceed the MIDI standard range
2230 if (highest_note_in_selection + dnote > 127) {
2231 highest_note_difference = highest_note_in_selection - 127;
2234 start_note_diff_command (_("move notes"));
2236 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2238 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2244 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2246 uint8_t original_pitch = (*i)->note()->note();
2247 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2249 // keep notes in standard midi range
2250 clamp_to_0_127(new_pitch);
2252 // keep original pitch if note is dragged outside valid midi range
2253 if ((original_pitch != 0 && new_pitch == 0)
2254 || (original_pitch != 127 && new_pitch == 127)) {
2255 new_pitch = original_pitch;
2258 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2259 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2261 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2266 // care about notes being moved beyond the upper/lower bounds on the canvas
2267 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2268 highest_note_in_selection > midi_stream_view()->highest_note()) {
2269 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2274 MidiRegionView::snap_pixel_to_frame(double x)
2276 PublicEditor& editor (trackview.editor());
2277 return snap_frame_to_frame (editor.pixel_to_frame (x));
2280 /** Snap a frame offset within our region using the current snap settings.
2281 * @param x Frame offset from this region's position.
2282 * @return Snapped frame offset from this region's position.
2285 MidiRegionView::snap_frame_to_frame (frameoffset_t x)
2287 PublicEditor& editor = trackview.editor();
2289 /* x is region relative, convert it to global absolute frames */
2290 framepos_t const session_frame = x + _region->position();
2292 /* try a snap in either direction */
2293 framepos_t frame = session_frame;
2294 editor.snap_to (frame, 0);
2296 /* if we went off the beginning of the region, snap forwards */
2297 if (frame < _region->position ()) {
2298 frame = session_frame;
2299 editor.snap_to (frame, 1);
2302 /* back to region relative */
2303 return frame - _region->position();
2307 MidiRegionView::snap_to_pixel(double x)
2309 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2313 MidiRegionView::get_position_pixels()
2315 framepos_t region_frame = get_position();
2316 return trackview.editor().frame_to_pixel(region_frame);
2320 MidiRegionView::get_end_position_pixels()
2322 framepos_t frame = get_position() + get_duration ();
2323 return trackview.editor().frame_to_pixel(frame);
2327 MidiRegionView::beats_to_frames(double beats) const
2329 return _time_converter.to(beats);
2333 MidiRegionView::frames_to_beats(framepos_t frames) const
2335 return _time_converter.from(frames);
2339 MidiRegionView::begin_resizing (bool /*at_front*/)
2341 _resize_data.clear();
2343 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2344 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2346 // only insert CanvasNotes into the map
2348 NoteResizeData *resize_data = new NoteResizeData();
2349 resize_data->canvas_note = note;
2351 // create a new SimpleRect from the note which will be the resize preview
2352 SimpleRect *resize_rect = new SimpleRect(
2353 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2355 // calculate the colors: get the color settings
2356 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2357 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2360 // make the resize preview notes more transparent and bright
2361 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2363 // calculate color based on note velocity
2364 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2365 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2369 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2370 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2372 resize_data->resize_rect = resize_rect;
2373 _resize_data.push_back(resize_data);
2378 /** Update resizing notes while user drags.
2379 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2380 * @param at_front which end of the note (true == note on, false == note off)
2381 * @param delta_x change in mouse position since the start of the drag
2382 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2383 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2384 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2385 * as the \a primary note.
2388 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2390 bool cursor_set = false;
2392 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2393 SimpleRect* resize_rect = (*i)->resize_rect;
2394 CanvasNote* canvas_note = (*i)->canvas_note;
2399 current_x = canvas_note->x1() + delta_x;
2401 current_x = primary->x1() + delta_x;
2405 current_x = canvas_note->x2() + delta_x;
2407 current_x = primary->x2() + delta_x;
2412 resize_rect->property_x1() = snap_to_pixel(current_x);
2413 resize_rect->property_x2() = canvas_note->x2();
2415 resize_rect->property_x2() = snap_to_pixel(current_x);
2416 resize_rect->property_x1() = canvas_note->x1();
2422 beats = snap_pixel_to_frame (current_x);
2423 beats = frames_to_beats (beats);
2428 if (beats < canvas_note->note()->end_time()) {
2429 len = canvas_note->note()->time() - beats;
2430 len += canvas_note->note()->length();
2435 if (beats >= canvas_note->note()->time()) {
2436 len = beats - canvas_note->note()->time();
2443 snprintf (buf, sizeof (buf), "%.3g beats", len);
2444 show_verbose_cursor (buf, 0, 0);
2453 /** Finish resizing notes when the user releases the mouse button.
2454 * Parameters the same as for \a update_resizing().
2457 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2459 start_note_diff_command (_("resize notes"));
2461 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2462 CanvasNote* canvas_note = (*i)->canvas_note;
2463 SimpleRect* resize_rect = (*i)->resize_rect;
2468 current_x = canvas_note->x1() + delta_x;
2470 current_x = primary->x1() + delta_x;
2474 current_x = canvas_note->x2() + delta_x;
2476 current_x = primary->x2() + delta_x;
2480 current_x = snap_pixel_to_frame (current_x);
2481 current_x = frames_to_beats (current_x);
2483 if (at_front && current_x < canvas_note->note()->end_time()) {
2484 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2486 double len = canvas_note->note()->time() - current_x;
2487 len += canvas_note->note()->length();
2490 /* XXX convert to beats */
2491 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2496 double len = current_x - canvas_note->note()->time();
2499 /* XXX convert to beats */
2500 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2508 _resize_data.clear();
2513 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2515 uint8_t new_velocity;
2518 new_velocity = event->note()->velocity() + velocity;
2519 clamp_to_0_127(new_velocity);
2521 new_velocity = velocity;
2524 event->set_selected (event->selected()); // change color
2526 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2530 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2535 new_note = event->note()->note() + note;
2540 clamp_to_0_127 (new_note);
2541 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2545 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2547 bool change_start = false;
2548 bool change_length = false;
2549 Evoral::MusicalTime new_start = 0;
2550 Evoral::MusicalTime new_length = 0;
2552 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2554 front_delta: if positive - move the start of the note later in time (shortening it)
2555 if negative - move the start of the note earlier in time (lengthening it)
2557 end_delta: if positive - move the end of the note later in time (lengthening it)
2558 if negative - move the end of the note earlier in time (shortening it)
2562 if (front_delta < 0) {
2564 if (event->note()->time() < -front_delta) {
2567 new_start = event->note()->time() + front_delta; // moves earlier
2570 /* start moved toward zero, so move the end point out to where it used to be.
2571 Note that front_delta is negative, so this increases the length.
2574 new_length = event->note()->length() - front_delta;
2575 change_start = true;
2576 change_length = true;
2580 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2582 if (new_pos < event->note()->end_time()) {
2583 new_start = event->note()->time() + front_delta;
2584 /* start moved toward the end, so move the end point back to where it used to be */
2585 new_length = event->note()->length() - front_delta;
2586 change_start = true;
2587 change_length = true;
2594 bool can_change = true;
2595 if (end_delta < 0) {
2596 if (event->note()->length() < -end_delta) {
2602 new_length = event->note()->length() + end_delta;
2603 change_length = true;
2608 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2611 if (change_length) {
2612 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2617 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2619 uint8_t new_channel;
2623 if (event->note()->channel() < -chn) {
2626 new_channel = event->note()->channel() + chn;
2629 new_channel = event->note()->channel() + chn;
2632 new_channel = (uint8_t) chn;
2635 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2639 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2641 Evoral::MusicalTime new_time;
2645 if (event->note()->time() < -delta) {
2648 new_time = event->note()->time() + delta;
2651 new_time = event->note()->time() + delta;
2657 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2661 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2663 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2667 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2671 if (_selection.empty()) {
2686 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2687 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2693 start_note_diff_command (_("change velocities"));
2695 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2696 Selection::iterator next = i;
2698 change_note_velocity (*i, delta, true);
2704 if (!_selection.empty()) {
2706 snprintf (buf, sizeof (buf), "Vel %d",
2707 (int) (*_selection.begin())->note()->velocity());
2708 show_verbose_cursor (buf, 10, 10);
2714 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2716 if (_selection.empty()) {
2733 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2735 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2739 if ((int8_t) (*i)->note()->note() + delta > 127) {
2746 start_note_diff_command (_("transpose"));
2748 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2749 Selection::iterator next = i;
2751 change_note_note (*i, delta, true);
2759 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2765 /* grab the current grid distance */
2767 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2769 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2770 cerr << "Grid type not available as beats - TO BE FIXED\n";
2780 start_note_diff_command (_("change note lengths"));
2782 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2783 Selection::iterator next = i;
2786 /* note the negation of the delta for start */
2788 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2797 MidiRegionView::nudge_notes (bool forward)
2799 if (_selection.empty()) {
2803 /* pick a note as the point along the timeline to get the nudge distance.
2804 its not necessarily the earliest note, so we may want to pull the notes out
2805 into a vector and sort before using the first one.
2808 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2810 framepos_t distance;
2812 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2814 /* grid is off - use nudge distance */
2816 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2822 framepos_t next_pos = ref_point;
2825 if (max_framepos - 1 < next_pos) {
2829 if (next_pos == 0) {
2835 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2836 distance = ref_point - next_pos;
2839 if (distance == 0) {
2843 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2849 start_note_diff_command (_("nudge"));
2851 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2852 Selection::iterator next = i;
2854 change_note_time (*i, delta, true);
2862 MidiRegionView::change_channel(uint8_t channel)
2864 start_note_diff_command(_("change channel"));
2865 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2866 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2874 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2876 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2878 _pre_enter_cursor = editor->get_canvas_cursor ();
2880 if (_mouse_state == SelectTouchDragging) {
2881 note_selected (ev, true);
2884 show_verbose_cursor (ev->note ());
2888 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2890 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2892 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2893 (*i)->hide_velocity ();
2896 editor->verbose_cursor()->hide ();
2898 if (_pre_enter_cursor) {
2899 editor->set_canvas_cursor (_pre_enter_cursor);
2900 _pre_enter_cursor = 0;
2905 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
2908 s << ((int) ev->patch()->program() + 1) << ":" << (ev->patch()->bank() + 1);
2909 show_verbose_cursor (s.str(), 10, 20);
2913 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
2915 trackview.editor().verbose_cursor()->hide ();
2919 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
2921 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2923 if (x_fraction > 0.0 && x_fraction < 0.25) {
2924 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
2925 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
2926 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
2928 if (_pre_enter_cursor && can_set_cursor) {
2929 editor->set_canvas_cursor (_pre_enter_cursor);
2935 MidiRegionView::set_frame_color()
2939 TimeAxisViewItem::set_frame_color ();
2946 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2947 } else if (high_enough_for_name) {
2948 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2953 if (!rect_visible) {
2954 f = UINT_RGBA_CHANGE_A (f, 0);
2957 frame->property_fill_color_rgba() = f;
2961 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2965 case FilterChannels:
2966 _force_channel = -1;
2969 _force_channel = mask;
2970 mask = 0xFFFF; // Show all notes as active (below)
2973 // Update notes for selection
2974 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2975 (*i)->on_channel_selection_change(mask);
2978 _last_channel_selection = mask;
2980 _patch_changes.clear ();
2981 display_patch_changes ();
2985 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2987 _model_name = model;
2988 _custom_device_mode = custom_device_mode;
2993 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2995 if (_selection.empty()) {
2999 PublicEditor& editor (trackview.editor());
3003 /* XXX what to do ? */
3007 editor.get_cut_buffer().add (selection_as_cut_buffer());
3015 start_note_diff_command();
3017 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3024 note_diff_remove_note (*i);
3034 MidiRegionView::selection_as_cut_buffer () const
3038 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3039 NoteType* n = (*i)->note().get();
3040 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3043 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3049 /** This method handles undo */
3051 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3057 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3059 trackview.session()->begin_reversible_command (_("paste"));
3061 start_note_diff_command (_("paste"));
3063 Evoral::MusicalTime beat_delta;
3064 Evoral::MusicalTime paste_pos_beats;
3065 Evoral::MusicalTime duration;
3066 Evoral::MusicalTime end_point = 0;
3068 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3069 paste_pos_beats = frames_to_beats (pos - _region->position());
3070 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3071 paste_pos_beats = 0;
3073 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",
3074 (*mcb.notes().begin())->time(),
3075 (*mcb.notes().rbegin())->end_time(),
3076 duration, pos, _region->position(),
3077 paste_pos_beats, beat_delta));
3081 for (int n = 0; n < (int) times; ++n) {
3083 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3085 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3086 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3088 /* make all newly added notes selected */
3090 note_diff_add_note (copied_note, true);
3091 end_point = copied_note->end_time();
3094 paste_pos_beats += duration;
3097 /* if we pasted past the current end of the region, extend the region */
3099 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
3100 framepos_t region_end = _region->position() + _region->length() - 1;
3102 if (end_frame > region_end) {
3104 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3106 _region->clear_changes ();
3107 _region->set_length (end_frame);
3108 trackview.session()->add_command (new StatefulDiffCommand (_region));
3113 trackview.session()->commit_reversible_command ();
3116 struct EventNoteTimeEarlyFirstComparator {
3117 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3118 return a->note()->time() < b->note()->time();
3123 MidiRegionView::time_sort_events ()
3125 if (!_sort_needed) {
3129 EventNoteTimeEarlyFirstComparator cmp;
3132 _sort_needed = false;
3136 MidiRegionView::goto_next_note ()
3138 // framepos_t pos = -1;
3139 bool use_next = false;
3141 if (_events.back()->selected()) {
3145 time_sort_events ();
3147 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3148 if ((*i)->selected()) {
3151 } else if (use_next) {
3153 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3158 /* use the first one */
3160 unique_select (_events.front());
3165 MidiRegionView::goto_previous_note ()
3167 // framepos_t pos = -1;
3168 bool use_next = false;
3170 if (_events.front()->selected()) {
3174 time_sort_events ();
3176 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3177 if ((*i)->selected()) {
3180 } else if (use_next) {
3182 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3187 /* use the last one */
3189 unique_select (*(_events.rbegin()));
3193 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3195 bool had_selected = false;
3197 time_sort_events ();
3199 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3200 if ((*i)->selected()) {
3201 selected.insert ((*i)->note());
3202 had_selected = true;
3206 if (allow_all_if_none_selected && !had_selected) {
3207 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3208 selected.insert ((*i)->note());
3214 MidiRegionView::update_ghost_note (double x, double y)
3216 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3221 _note_group->w2i (x, y);
3222 framepos_t const f = snap_pixel_to_frame (x);
3225 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
3231 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
3233 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
3234 _ghost_note->note()->set_length (length);
3235 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3236 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3238 /* the ghost note does not appear in ghost regions, so pass false in here */
3239 update_note (_ghost_note, false);
3241 show_verbose_cursor (_ghost_note->note ());
3245 MidiRegionView::create_ghost_note (double x, double y)
3250 boost::shared_ptr<NoteType> g (new NoteType);
3251 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3252 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3253 update_ghost_note (x, y);
3254 _ghost_note->show ();
3259 show_verbose_cursor (_ghost_note->note ());
3263 MidiRegionView::snap_changed ()
3269 create_ghost_note (_last_ghost_x, _last_ghost_y);
3273 MidiRegionView::drop_down_keys ()
3275 _mouse_state = None;
3279 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3281 double note = midi_stream_view()->y_to_note(y);
3283 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3285 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3287 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3288 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3289 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3290 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3295 bool add_mrv_selection = false;
3297 if (_selection.empty()) {
3298 add_mrv_selection = true;
3301 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3302 if (_selection.insert (*i).second) {
3303 (*i)->set_selected (true);
3307 if (add_mrv_selection) {
3308 PublicEditor& editor (trackview.editor());
3309 editor.get_selection().add (this);
3314 MidiRegionView::color_handler ()
3316 RegionView::color_handler ();
3318 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3319 (*i)->set_selected ((*i)->selected()); // will change color
3322 /* XXX probably more to do here */
3326 MidiRegionView::enable_display (bool yn)
3328 RegionView::enable_display (yn);
3335 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3337 if (_step_edit_cursor == 0) {
3338 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3340 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3341 _step_edit_cursor->property_y1() = 0;
3342 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3343 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3344 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3347 move_step_edit_cursor (pos);
3348 _step_edit_cursor->show ();
3352 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3354 _step_edit_cursor_position = pos;
3356 if (_step_edit_cursor) {
3357 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3358 _step_edit_cursor->property_x1() = pixel;
3359 set_step_edit_cursor_width (_step_edit_cursor_width);
3364 MidiRegionView::hide_step_edit_cursor ()
3366 if (_step_edit_cursor) {
3367 _step_edit_cursor->hide ();
3372 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3374 _step_edit_cursor_width = beats;
3376 if (_step_edit_cursor) {
3377 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
3381 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3382 * @param buf Data that has been recorded.
3383 * @param w Source that this data will end up in.
3386 MidiRegionView::data_recorded (boost::shared_ptr<MidiBuffer> buf, boost::weak_ptr<MidiSource> w)
3388 if (!_active_notes) {
3389 /* we aren't actively being recorded to */
3393 boost::shared_ptr<MidiSource> src = w.lock ();
3394 if (!src || src != midi_region()->midi_source()) {
3395 /* recorded data was not destined for our source */
3399 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3400 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3402 framepos_t back = max_framepos;
3404 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3405 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3406 assert (ev.buffer ());
3408 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3410 if (ev.type() == MIDI_CMD_NOTE_ON) {
3412 boost::shared_ptr<NoteType> note (
3413 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3416 add_note (note, true);
3418 /* fix up our note range */
3419 if (ev.note() < _current_range_min) {
3420 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3421 } else if (ev.note() > _current_range_max) {
3422 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3425 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3426 resolve_note (ev.note (), time_beats);
3432 midi_stream_view()->check_record_layers (region(), back);
3436 MidiRegionView::trim_front_starting ()
3438 /* Reparent the note group to the region view's parent, so that it doesn't change
3439 when the region view is trimmed.
3441 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3442 _temporary_note_group->move (group->property_x(), group->property_y());
3443 _note_group->reparent (*_temporary_note_group);
3447 MidiRegionView::trim_front_ending ()
3449 _note_group->reparent (*group);
3450 delete _temporary_note_group;
3451 _temporary_note_group = 0;
3453 if (_region->start() < 0) {
3454 /* Trim drag made start time -ve; fix this */
3455 midi_region()->fix_negative_start ();
3460 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3462 PatchChangeDialog d (&_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3463 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3467 change_patch_change (pc->patch(), d.patch ());
3472 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3475 snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3476 Evoral::midi_note_name (n->note()).c_str(),
3478 (int) n->channel() + 1,
3479 (int) n->velocity());
3481 show_verbose_cursor (buf, 10, 20);
3485 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3489 trackview.editor().get_pointer_position (wx, wy);
3494 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3496 double x1, y1, x2, y2;
3497 trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3499 if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3500 wy -= (y2 - y1) + 2 * yoffset;
3503 trackview.editor().verbose_cursor()->set (text, wx, wy);
3504 trackview.editor().verbose_cursor()->show ();