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 "editor_drag.h"
55 #include "ghostregion.h"
56 #include "gui_thread.h"
58 #include "midi_channel_dialog.h"
59 #include "midi_cut_buffer.h"
60 #include "midi_list_editor.h"
61 #include "midi_region_view.h"
62 #include "midi_streamview.h"
63 #include "midi_time_axis.h"
64 #include "midi_util.h"
65 #include "note_player.h"
66 #include "public_editor.h"
67 #include "rgb_macros.h"
68 #include "selection.h"
69 #include "simpleline.h"
70 #include "streamview.h"
72 #include "mouse_cursors.h"
73 #include "patch_change_dialog.h"
74 #include "verbose_cursor.h"
78 using namespace ARDOUR;
80 using namespace Editing;
81 using namespace ArdourCanvas;
82 using Gtkmm2ext::Keyboard;
84 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
86 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
88 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
89 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
90 : RegionView (parent, tv, r, spu, basic_color)
92 , _last_channel_selection(0xFFFF)
93 , _current_range_min(0)
94 , _current_range_max(0)
95 , _model_name(string())
96 , _custom_device_mode(string())
98 , _note_group(new ArdourCanvas::Group(*group))
99 , _note_diff_command (0)
102 , _step_edit_cursor (0)
103 , _step_edit_cursor_width (1.0)
104 , _step_edit_cursor_position (0.0)
105 , _channel_selection_scoped_note (0)
106 , _temporary_note_group (0)
109 , _sort_needed (true)
110 , _optimization_iterator (_events.end())
112 , _no_sound_notes (false)
115 , _pre_enter_cursor (0)
117 _note_group->raise_to_top();
118 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
120 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
121 connect_to_diskstream ();
123 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
126 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
127 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
128 TimeAxisViewItem::Visibility visibility)
129 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
131 , _last_channel_selection(0xFFFF)
132 , _model_name(string())
133 , _custom_device_mode(string())
135 , _note_group(new ArdourCanvas::Group(*parent))
136 , _note_diff_command (0)
139 , _step_edit_cursor (0)
140 , _step_edit_cursor_width (1.0)
141 , _step_edit_cursor_position (0.0)
142 , _channel_selection_scoped_note (0)
143 , _temporary_note_group (0)
146 , _sort_needed (true)
147 , _optimization_iterator (_events.end())
149 , _no_sound_notes (false)
153 _note_group->raise_to_top();
154 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
156 connect_to_diskstream ();
158 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
162 MidiRegionView::parameter_changed (std::string const & p)
164 if (p == "diplay-first-midi-bank-as-zero") {
165 if (_enable_display) {
171 MidiRegionView::MidiRegionView (const MidiRegionView& other)
172 : sigc::trackable(other)
175 , _last_channel_selection(0xFFFF)
176 , _model_name(string())
177 , _custom_device_mode(string())
179 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
180 , _note_diff_command (0)
183 , _step_edit_cursor (0)
184 , _step_edit_cursor_width (1.0)
185 , _step_edit_cursor_position (0.0)
186 , _channel_selection_scoped_note (0)
187 , _temporary_note_group (0)
190 , _sort_needed (true)
191 , _optimization_iterator (_events.end())
193 , _no_sound_notes (false)
200 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
201 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
206 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
207 : RegionView (other, boost::shared_ptr<Region> (region))
209 , _last_channel_selection(0xFFFF)
210 , _model_name(string())
211 , _custom_device_mode(string())
213 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
214 , _note_diff_command (0)
217 , _step_edit_cursor (0)
218 , _step_edit_cursor_width (1.0)
219 , _step_edit_cursor_position (0.0)
220 , _channel_selection_scoped_note (0)
221 , _temporary_note_group (0)
224 , _sort_needed (true)
225 , _optimization_iterator (_events.end())
227 , _no_sound_notes (false)
234 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
235 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
241 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
243 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
245 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
246 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
250 midi_region()->midi_source(0)->load_model();
253 _model = midi_region()->midi_source(0)->model();
254 _enable_display = false;
256 RegionView::init (basic_color, false);
258 compute_colors (basic_color);
260 set_height (trackview.current_height());
263 region_sync_changed ();
264 region_resized (ARDOUR::bounds_change);
267 reset_width_dependent_items (_pixel_width);
271 _enable_display = true;
274 display_model (_model);
278 group->raise_to_top();
279 group->signal_event().connect(
280 sigc::mem_fun(this, &MidiRegionView::canvas_event), false);
282 midi_view()->signal_channel_mode_changed().connect(
283 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
285 midi_view()->signal_midi_patch_settings_changed().connect(
286 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
288 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
289 ui_bind(&MidiRegionView::snap_changed, this),
292 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
293 connect_to_diskstream ();
295 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
298 const boost::shared_ptr<ARDOUR::MidiRegion>
299 MidiRegionView::midi_region() const
301 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
305 MidiRegionView::connect_to_diskstream ()
307 midi_view()->midi_track()->DataRecorded.connect(
308 *this, invalidator(*this),
309 ui_bind(&MidiRegionView::data_recorded, this, _1),
314 MidiRegionView::canvas_event(GdkEvent* ev)
317 case GDK_ENTER_NOTIFY:
318 case GDK_LEAVE_NOTIFY:
319 _last_event_x = ev->crossing.x;
320 _last_event_y = ev->crossing.y;
322 case GDK_MOTION_NOTIFY:
323 _last_event_x = ev->motion.x;
324 _last_event_y = ev->motion.y;
330 if (!trackview.editor().internal_editing()) {
336 return scroll (&ev->scroll);
339 return key_press (&ev->key);
341 case GDK_KEY_RELEASE:
342 return key_release (&ev->key);
344 case GDK_BUTTON_PRESS:
345 return button_press (&ev->button);
347 case GDK_2BUTTON_PRESS:
350 case GDK_BUTTON_RELEASE:
351 return button_release (&ev->button);
353 case GDK_ENTER_NOTIFY:
354 return enter_notify (&ev->crossing);
356 case GDK_LEAVE_NOTIFY:
357 return leave_notify (&ev->crossing);
359 case GDK_MOTION_NOTIFY:
360 return motion (&ev->motion);
370 MidiRegionView::remove_ghost_note ()
377 MidiRegionView::enter_notify (GdkEventCrossing* ev)
379 trackview.editor().MouseModeChanged.connect (
380 _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
383 if (trackview.editor().current_mouse_mode() == MouseRange) {
384 create_ghost_note (ev->x, ev->y);
387 if (!trackview.editor().internal_editing()) {
388 Keyboard::magic_widget_drop_focus();
390 Keyboard::magic_widget_grab_focus();
398 MidiRegionView::leave_notify (GdkEventCrossing*)
400 _mouse_mode_connection.disconnect ();
402 trackview.editor().verbose_cursor()->hide ();
403 remove_ghost_note ();
409 MidiRegionView::mouse_mode_changed ()
411 if (trackview.editor().current_mouse_mode() == MouseRange && trackview.editor().internal_editing()) {
412 create_ghost_note (_last_event_x, _last_event_y);
414 remove_ghost_note ();
415 trackview.editor().verbose_cursor()->hide ();
418 if (!trackview.editor().internal_editing()) {
419 Keyboard::magic_widget_drop_focus();
421 Keyboard::magic_widget_grab_focus();
427 MidiRegionView::button_press (GdkEventButton* ev)
429 if (ev->button != 1) {
436 group->w2i (_last_x, _last_y);
438 if (_mouse_state != SelectTouchDragging) {
440 _pressed_button = ev->button;
441 _mouse_state = Pressed;
446 _pressed_button = ev->button;
452 MidiRegionView::button_release (GdkEventButton* ev)
454 double event_x, event_y;
456 if (ev->button != 1) {
463 group->w2i(event_x, event_y);
464 group->ungrab(ev->time);
466 PublicEditor& editor = trackview.editor ();
468 switch (_mouse_state) {
469 case Pressed: // Clicked
471 switch (editor.current_mouse_mode()) {
477 if (Keyboard::is_insert_note_event(ev)) {
479 double event_x, event_y;
483 group->w2i(event_x, event_y);
486 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
492 create_note_at (event_x, event_y, beats, true, true);
500 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
506 create_note_at (event_x, event_y, beats, true, true);
517 case SelectRectDragging: // Select drag done
518 editor.drags()->end_grab ((GdkEvent *) ev);
522 case AddDragging: // Add drag done
526 if (Keyboard::is_insert_note_event(ev) || trackview.editor().current_mouse_mode() == MouseRange) {
528 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
530 const double x = _drag_rect->property_x1();
531 const double length = trackview.editor().pixel_to_frame (_drag_rect->property_x2() - _drag_rect->property_x1());
533 create_note_at (x, _drag_rect->property_y1(), region_frames_to_region_beats(length), true, false);
540 create_ghost_note (ev->x, ev->y);
550 MidiRegionView::motion (GdkEventMotion* ev)
552 double event_x, event_y;
553 framepos_t event_frame = 0;
557 group->w2i(event_x, event_y);
559 PublicEditor& editor = trackview.editor ();
561 // convert event_x to global frame
562 framecnt_t grid_frames;
563 event_frame = snap_frame_to_grid_underneath (editor.pixel_to_frame (event_x), grid_frames);
565 if (!_ghost_note && editor.current_mouse_mode() != MouseRange
566 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())
567 && _mouse_state != AddDragging) {
569 create_ghost_note (ev->x, ev->y);
570 } else if (_ghost_note && editor.current_mouse_mode() != MouseRange
571 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
573 update_ghost_note (ev->x, ev->y);
574 } else if (_ghost_note && editor.current_mouse_mode() != MouseRange) {
579 editor.verbose_cursor()->hide ();
580 } else if (_ghost_note && editor.current_mouse_mode() == MouseRange) {
581 update_ghost_note (ev->x, ev->y);
584 /* any motion immediately hides velocity text that may have been visible */
586 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
587 (*i)->hide_velocity ();
590 switch (_mouse_state) {
591 case Pressed: // Maybe start a drag, if we've moved a bit
593 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
594 /* no appreciable movement since the button was pressed */
598 if (_pressed_button == 1 && editor.current_mouse_mode() == MouseObject
599 && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
601 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
602 _mouse_state = SelectRectDragging;
605 } else if (editor.internal_editing()) {
606 // Add note drag start
608 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
609 Gdk::Cursor(Gdk::FLEUR), ev->time);
613 _drag_start_x = event_x;
614 _drag_start_y = event_y;
616 _drag_rect = new ArdourCanvas::SimpleRect(*group);
617 _drag_rect->property_x1() = editor.frame_to_pixel(event_frame);
619 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
620 midi_stream_view()->y_to_note(event_y));
621 _drag_rect->property_x2() = editor.frame_to_pixel(event_frame);
623 _drag_rect->property_y2() = _drag_rect->property_y1()
624 + floor(midi_stream_view()->note_height());
625 _drag_rect->property_outline_what() = 0xFF;
626 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
627 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
629 _mouse_state = AddDragging;
634 editor.verbose_cursor()->hide ();
641 case SelectRectDragging:
642 editor.drags()->motion_handler ((GdkEvent *) ev, false);
645 case AddDragging: // Add note drag motion
650 GdkModifierType state;
651 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
656 if (_mouse_state == AddDragging) {
657 event_x = editor.frame_to_pixel(event_frame);
659 if (editor.snap_mode() == SnapNormal) {
660 /* event_frame will have been snapped to the start of the note we are under;
661 it's more intuitive if we use the end of that note here
663 event_x = editor.frame_to_pixel (event_frame + grid_frames);
665 event_x = editor.frame_to_pixel (event_frame);
672 if (event_x > _drag_start_x) {
673 _drag_rect->property_x2() = event_x;
676 _drag_rect->property_x1() = event_x;
683 case SelectTouchDragging:
695 MidiRegionView::scroll (GdkEventScroll* ev)
697 if (_selection.empty()) {
701 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
702 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
703 it still works for zoom.
708 trackview.editor().verbose_cursor()->hide ();
710 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
712 if (ev->direction == GDK_SCROLL_UP) {
713 change_velocities (true, fine, false);
714 } else if (ev->direction == GDK_SCROLL_DOWN) {
715 change_velocities (false, fine, false);
721 MidiRegionView::key_press (GdkEventKey* ev)
723 /* since GTK bindings are generally activated on press, and since
724 detectable auto-repeat is the name of the game and only sends
725 repeated presses, carry out key actions at key press, not release.
728 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
730 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
731 _mouse_state = SelectTouchDragging;
734 } else if (ev->keyval == GDK_Escape && unmodified) {
738 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
740 bool start = (ev->keyval == GDK_comma);
741 bool end = (ev->keyval == GDK_period);
742 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
743 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
745 change_note_lengths (fine, shorter, 0.0, start, end);
749 } else if (ev->keyval == GDK_Delete && unmodified) {
754 } else if (ev->keyval == GDK_Tab) {
756 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
757 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
759 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
763 } else if (ev->keyval == GDK_ISO_Left_Tab) {
765 /* Shift-TAB generates ISO Left Tab, for some reason */
767 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
768 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
770 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
776 } else if (ev->keyval == GDK_Up) {
778 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
779 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
781 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
782 change_velocities (true, fine, allow_smush);
784 transpose (true, fine, allow_smush);
788 } else if (ev->keyval == GDK_Down) {
790 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
791 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
793 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
794 change_velocities (false, fine, allow_smush);
796 transpose (false, fine, allow_smush);
800 } else if (ev->keyval == GDK_Left && unmodified) {
805 } else if (ev->keyval == GDK_Right && unmodified) {
810 } else if (ev->keyval == GDK_c && unmodified) {
819 MidiRegionView::key_release (GdkEventKey* ev)
821 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
829 MidiRegionView::channel_edit ()
832 uint8_t current_channel = 0;
834 if (_selection.empty()) {
838 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
840 current_channel = (*i)->note()->channel ();
845 MidiChannelDialog channel_dialog (current_channel);
846 int ret = channel_dialog.run ();
849 case Gtk::RESPONSE_OK:
855 uint8_t new_channel = channel_dialog.active_channel ();
857 start_note_diff_command (_("channel edit"));
859 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
860 Selection::iterator next = i;
862 change_note_channel (*i, new_channel);
870 MidiRegionView::show_list_editor ()
873 _list_editor = new MidiListEditor (trackview.session(), midi_region());
875 _list_editor->present ();
878 /** Add a note to the model, and the view, at a canvas (click) coordinate.
879 * \param x horizontal position in pixels
880 * \param y vertical position in pixels
881 * \param length duration of the note in beats, which will be snapped to the grid
882 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
883 * \param snap_x true to snap x to the grid, otherwise false.
886 MidiRegionView::create_note_at (double x, double y, double length, bool sh, bool snap_x)
888 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
889 MidiStreamView* const view = mtv->midi_view();
891 double note = view->y_to_note(y);
894 assert(note <= 127.0);
896 // Start of note in frames relative to region start
897 framepos_t start_frames = trackview.editor().pixel_to_frame (x);
899 framecnt_t grid_frames;
900 start_frames = snap_frame_to_grid_underneath (start_frames, grid_frames);
902 assert(start_frames >= 0);
905 length = region_frames_to_region_beats(
906 snap_frame_to_frame (start_frames + region_beats_to_region_frames(length)) - start_frames);
908 assert (length != 0);
911 length = region_frames_to_region_beats (region_beats_to_region_frames (length) - 1);
914 const boost::shared_ptr<NoteType> new_note (new NoteType (mtv->get_channel_for_add (),
915 region_frames_to_region_beats(start_frames + _region->start()), length,
916 (uint8_t)note, 0x40));
918 if (_model->contains (new_note)) {
922 view->update_note_range(new_note->note());
924 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note");
926 _model->apply_command(*trackview.session(), cmd);
928 play_midi_note (new_note);
932 MidiRegionView::clear_events()
937 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
938 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
943 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
948 _patch_changes.clear();
950 _optimization_iterator = _events.end();
954 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
958 content_connection.disconnect ();
959 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
963 if (_enable_display) {
969 MidiRegionView::start_note_diff_command (string name)
971 if (!_note_diff_command) {
972 _note_diff_command = _model->new_note_diff_command (name);
977 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
979 if (_note_diff_command) {
980 _note_diff_command->add (note);
983 _marked_for_selection.insert(note);
986 _marked_for_velocity.insert(note);
991 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
993 if (_note_diff_command && ev->note()) {
994 _note_diff_command->remove(ev->note());
999 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1000 MidiModel::NoteDiffCommand::Property property,
1003 if (_note_diff_command) {
1004 _note_diff_command->change (ev->note(), property, val);
1009 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1010 MidiModel::NoteDiffCommand::Property property,
1011 Evoral::MusicalTime val)
1013 if (_note_diff_command) {
1014 _note_diff_command->change (ev->note(), property, val);
1019 MidiRegionView::apply_diff (bool as_subcommand)
1023 if (!_note_diff_command) {
1027 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1028 // Mark all selected notes for selection when model reloads
1029 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1030 _marked_for_selection.insert((*i)->note());
1034 if (as_subcommand) {
1035 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1037 _model->apply_command (*trackview.session(), _note_diff_command);
1040 _note_diff_command = 0;
1041 midi_view()->midi_track()->playlist_modified();
1043 if (add_or_remove) {
1044 _marked_for_selection.clear();
1047 _marked_for_velocity.clear();
1051 MidiRegionView::abort_command()
1053 delete _note_diff_command;
1054 _note_diff_command = 0;
1059 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1061 if (_optimization_iterator != _events.end()) {
1062 ++_optimization_iterator;
1065 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1066 return *_optimization_iterator;
1069 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1070 if ((*_optimization_iterator)->note() == note) {
1071 return *_optimization_iterator;
1079 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1081 MidiModel::Notes notes;
1082 _model->get_notes (notes, op, val, chan_mask);
1084 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1085 CanvasNoteEvent* cne = find_canvas_note (*n);
1093 MidiRegionView::redisplay_model()
1095 // Don't redisplay the model if we're currently recording and displaying that
1096 if (_active_notes) {
1104 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1105 (*i)->invalidate ();
1108 MidiModel::ReadLock lock(_model->read_lock());
1110 MidiModel::Notes& notes (_model->notes());
1111 _optimization_iterator = _events.begin();
1113 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1115 boost::shared_ptr<NoteType> note (*n);
1116 CanvasNoteEvent* cne;
1119 if (note_in_region_range (note, visible)) {
1121 if ((cne = find_canvas_note (note)) != 0) {
1128 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1130 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1142 add_note (note, visible);
1147 if ((cne = find_canvas_note (note)) != 0) {
1155 /* remove note items that are no longer valid */
1157 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1158 if (!(*i)->valid ()) {
1160 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1161 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1163 gr->remove_note (*i);
1168 i = _events.erase (i);
1175 _patch_changes.clear();
1179 display_patch_changes ();
1181 _marked_for_selection.clear ();
1182 _marked_for_velocity.clear ();
1184 /* we may have caused _events to contain things out of order (e.g. if a note
1185 moved earlier or later). we don't generally need them in time order, but
1186 make a note that a sort is required for those cases that require it.
1189 _sort_needed = true;
1193 MidiRegionView::display_patch_changes ()
1195 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1196 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1198 for (uint8_t i = 0; i < 16; ++i) {
1199 if (chn_mask & (1<<i)) {
1200 display_patch_changes_on_channel (i);
1202 /* TODO gray-out patch instad of not displaying it */
1207 MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
1209 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1211 if ((*i)->channel() != channel) {
1215 MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
1217 boost::shared_ptr<MIDI::Name::Patch> patch =
1218 MIDI::Name::MidiPatchManager::instance().find_patch(
1219 _model_name, _custom_device_mode, channel, patch_key);
1222 add_canvas_patch_change (*i, patch->name());
1225 /* program and bank numbers are zero-based: convert to one-based: MIDI_BP_ZERO */
1226 snprintf (buf, 16, "%d %d", (*i)->program() + MIDI_BP_ZERO , (*i)->bank() + MIDI_BP_ZERO);
1227 add_canvas_patch_change (*i, buf);
1233 MidiRegionView::display_sysexes()
1235 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1236 Evoral::MusicalTime time = (*i)->time();
1241 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1242 str << int((*i)->buffer()[b]);
1243 if (b != (*i)->size() -1) {
1247 string text = str.str();
1249 const double x = trackview.editor().frame_to_pixel(source_beats_to_absolute_frames(time));
1251 double height = midi_stream_view()->contents_height();
1253 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1254 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1256 // Show unless patch change is beyond the region bounds
1257 if (time - _region->start() >= _region->length() || time < _region->start()) {
1263 _sys_exes.push_back(sysex);
1268 MidiRegionView::~MidiRegionView ()
1270 in_destructor = true;
1272 trackview.editor().verbose_cursor()->hide ();
1274 note_delete_connection.disconnect ();
1276 delete _list_editor;
1278 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1280 if (_active_notes) {
1288 delete _note_diff_command;
1289 delete _step_edit_cursor;
1290 delete _temporary_note_group;
1294 MidiRegionView::region_resized (const PropertyChange& what_changed)
1296 RegionView::region_resized(what_changed);
1298 if (what_changed.contains (ARDOUR::Properties::position)) {
1299 set_duration(_region->length(), 0);
1300 if (_enable_display) {
1307 MidiRegionView::reset_width_dependent_items (double pixel_width)
1309 RegionView::reset_width_dependent_items(pixel_width);
1310 assert(_pixel_width == pixel_width);
1312 if (_enable_display) {
1316 move_step_edit_cursor (_step_edit_cursor_position);
1317 set_step_edit_cursor_width (_step_edit_cursor_width);
1321 MidiRegionView::set_height (double height)
1323 static const double FUDGE = 2.0;
1324 const double old_height = _height;
1325 RegionView::set_height(height);
1326 _height = height - FUDGE;
1328 apply_note_range(midi_stream_view()->lowest_note(),
1329 midi_stream_view()->highest_note(),
1330 height != old_height + FUDGE);
1333 name_pixbuf->raise_to_top();
1336 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1337 (*x)->set_height (midi_stream_view()->contents_height());
1340 if (_step_edit_cursor) {
1341 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1346 /** Apply the current note range from the stream view
1347 * by repositioning/hiding notes as necessary
1350 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1352 if (!_enable_display) {
1356 if (!force && _current_range_min == min && _current_range_max == max) {
1360 _current_range_min = min;
1361 _current_range_max = max;
1363 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1364 CanvasNoteEvent* event = *i;
1365 boost::shared_ptr<NoteType> note (event->note());
1367 if (note->note() < _current_range_min ||
1368 note->note() > _current_range_max) {
1374 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1376 const double y1 = midi_stream_view()->note_to_y(note->note());
1377 const double y2 = y1 + floor(midi_stream_view()->note_height());
1379 cnote->property_y1() = y1;
1380 cnote->property_y2() = y2;
1382 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1384 const double diamond_size = update_hit (chit);
1386 chit->set_height (diamond_size);
1392 MidiRegionView::add_ghost (TimeAxisView& tv)
1396 double unit_position = _region->position () / samples_per_unit;
1397 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1398 MidiGhostRegion* ghost;
1400 if (mtv && mtv->midi_view()) {
1401 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1402 to allow having midi notes on top of note lines and waveforms.
1404 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1406 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1409 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1410 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1411 ghost->add_note(note);
1415 ghost->set_height ();
1416 ghost->set_duration (_region->length() / samples_per_unit);
1417 ghosts.push_back (ghost);
1419 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1425 /** Begin tracking note state for successive calls to add_event
1428 MidiRegionView::begin_write()
1430 assert(!_active_notes);
1431 _active_notes = new CanvasNote*[128];
1432 for (unsigned i=0; i < 128; ++i) {
1433 _active_notes[i] = 0;
1438 /** Destroy note state for add_event
1441 MidiRegionView::end_write()
1443 delete[] _active_notes;
1445 _marked_for_selection.clear();
1446 _marked_for_velocity.clear();
1450 /** Resolve an active MIDI note (while recording).
1453 MidiRegionView::resolve_note(uint8_t note, double end_time)
1455 if (midi_view()->note_mode() != Sustained) {
1459 if (_active_notes && _active_notes[note]) {
1461 /* XXX is end_time really region-centric? I think so, because
1462 this is a new region that we're recording, so source zero is
1463 the same as region zero
1465 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1467 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1468 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1469 _active_notes[note] = 0;
1474 /** Extend active notes to rightmost edge of region (if length is changed)
1477 MidiRegionView::extend_active_notes()
1479 if (!_active_notes) {
1483 for (unsigned i=0; i < 128; ++i) {
1484 if (_active_notes[i]) {
1485 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1492 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1494 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1498 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1500 if (!route_ui || !route_ui->midi_track()) {
1504 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1510 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1512 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1516 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1518 if (!route_ui || !route_ui->midi_track()) {
1522 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1524 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1533 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1535 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1536 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1538 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1539 (note->note() <= midi_stream_view()->highest_note());
1544 /** Update a canvas note's size from its model note.
1545 * @param ev Canvas note to update.
1546 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1549 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1551 boost::shared_ptr<NoteType> note = ev->note();
1553 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time()));
1554 const double y1 = midi_stream_view()->note_to_y(note->note());
1556 ev->property_x1() = x;
1557 ev->property_y1() = y1;
1559 /* trim note display to not overlap the end of its region */
1561 if (note->length() > 0) {
1562 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1563 ev->property_x2() = trackview.editor().frame_to_pixel (note_end_frames);
1565 ev->property_x2() = trackview.editor().frame_to_pixel (_region->length());
1568 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1570 if (note->length() == 0) {
1571 if (_active_notes) {
1572 assert(note->note() < 128);
1573 // If this note is already active there's a stuck note,
1574 // finish the old note rectangle
1575 if (_active_notes[note->note()]) {
1576 CanvasNote* const old_rect = _active_notes[note->note()];
1577 boost::shared_ptr<NoteType> old_note = old_rect->note();
1578 old_rect->property_x2() = x;
1579 old_rect->property_outline_what() = (guint32) 0xF;
1581 _active_notes[note->note()] = ev;
1583 /* outline all but right edge */
1584 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1586 /* outline all edges */
1587 ev->property_outline_what() = (guint32) 0xF;
1590 if (update_ghost_regions) {
1591 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1592 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1594 gr->update_note (ev);
1601 MidiRegionView::update_hit (CanvasHit* ev)
1603 boost::shared_ptr<NoteType> note = ev->note();
1605 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1606 const double x = trackview.editor().frame_to_pixel(note_start_frames);
1607 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1608 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1612 return diamond_size;
1615 /** Add a MIDI note to the view (with length).
1617 * If in sustained mode, notes with length 0 will be considered active
1618 * notes, and resolve_note should be called when the corresponding note off
1619 * event arrives, to properly display the note.
1622 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1624 CanvasNoteEvent* event = 0;
1626 assert(note->time() >= 0);
1627 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1629 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1631 if (midi_view()->note_mode() == Sustained) {
1633 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1635 update_note (ev_rect);
1639 MidiGhostRegion* gr;
1641 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1642 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1643 gr->add_note(ev_rect);
1647 } else if (midi_view()->note_mode() == Percussive) {
1649 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1651 CanvasHit* ev_diamond = new CanvasHit (*this, *_note_group, diamond_size, note);
1653 update_hit (ev_diamond);
1662 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1663 note_selected(event, true);
1666 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1667 event->show_velocity();
1670 event->on_channel_selection_change(_last_channel_selection);
1671 _events.push_back(event);
1680 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1681 MidiStreamView* const view = mtv->midi_view();
1683 view->update_note_range (note->note());
1687 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1688 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1690 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1692 /* potentially extend region to hold new note */
1694 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1695 framepos_t region_end = _region->last_frame();
1697 if (end_frame > region_end) {
1698 _region->set_length (end_frame - _region->position());
1701 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1702 MidiStreamView* const view = mtv->midi_view();
1704 view->update_note_range(new_note->note());
1706 _marked_for_selection.clear ();
1709 start_note_diff_command (_("step add"));
1710 note_diff_add_note (new_note, true, false);
1713 // last_step_edit_note = new_note;
1717 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1719 change_note_lengths (false, false, beats, false, true);
1723 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
1725 assert (patch->time() >= 0);
1727 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (patch->time()));
1729 double const height = midi_stream_view()->contents_height();
1731 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1732 new CanvasPatchChange(*this, *_note_group,
1737 _custom_device_mode,
1741 // Show unless patch change is beyond the region bounds
1742 if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1743 patch_change->hide();
1745 patch_change->show();
1748 _patch_changes.push_back (patch_change);
1752 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1754 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1755 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1759 if (i != _model->patch_changes().end()) {
1760 key.msb = (*i)->bank_msb ();
1761 key.lsb = (*i)->bank_lsb ();
1762 key.program_number = (*i)->program ();
1764 key.msb = key.lsb = key.program_number = 0;
1767 assert (key.is_sane());
1772 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1774 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1776 if (pc.patch()->program() != new_patch.program_number) {
1777 c->change_program (pc.patch (), new_patch.program_number);
1780 int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1781 if (pc.patch()->bank() != new_bank) {
1782 c->change_bank (pc.patch (), new_bank);
1785 _model->apply_command (*trackview.session(), c);
1787 _patch_changes.clear ();
1788 display_patch_changes ();
1792 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1794 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1796 if (old_change->time() != new_change.time()) {
1797 c->change_time (old_change, new_change.time());
1800 if (old_change->channel() != new_change.channel()) {
1801 c->change_channel (old_change, new_change.channel());
1804 if (old_change->program() != new_change.program()) {
1805 c->change_program (old_change, new_change.program());
1808 if (old_change->bank() != new_change.bank()) {
1809 c->change_bank (old_change, new_change.bank());
1812 _model->apply_command (*trackview.session(), c);
1814 _patch_changes.clear ();
1815 display_patch_changes ();
1818 /** Add a patch change to the region.
1819 * @param t Time in frames relative to region position
1820 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1821 * MidiTimeAxisView::get_channel_for_add())
1824 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1826 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1828 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1829 c->add (MidiModel::PatchChangePtr (
1830 new Evoral::PatchChange<Evoral::MusicalTime> (
1831 absolute_frames_to_source_beats (_region->position() + t),
1832 mtv->get_channel_for_add(), patch.program(), patch.bank()
1837 _model->apply_command (*trackview.session(), c);
1839 _patch_changes.clear ();
1840 display_patch_changes ();
1844 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1846 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1847 c->change_time (pc.patch (), t);
1848 _model->apply_command (*trackview.session(), c);
1850 _patch_changes.clear ();
1851 display_patch_changes ();
1855 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1857 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1858 c->remove (pc->patch ());
1859 _model->apply_command (*trackview.session(), c);
1861 _patch_changes.clear ();
1862 display_patch_changes ();
1866 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1868 if (patch.patch()->program() < 127) {
1869 MIDI::Name::PatchPrimaryKey key;
1870 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1871 key.program_number++;
1872 change_patch_change (patch, key);
1877 MidiRegionView::next_patch (CanvasPatchChange& patch)
1879 if (patch.patch()->program() > 0) {
1880 MIDI::Name::PatchPrimaryKey key;
1881 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1882 key.program_number--;
1883 change_patch_change (patch, key);
1888 MidiRegionView::previous_bank (CanvasPatchChange& patch)
1890 if (patch.patch()->program() < 127) {
1891 MIDI::Name::PatchPrimaryKey key;
1892 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1895 change_patch_change (patch, key);
1900 change_patch_change (patch, key);
1907 MidiRegionView::next_bank (CanvasPatchChange& patch)
1909 if (patch.patch()->program() > 0) {
1910 MIDI::Name::PatchPrimaryKey key;
1911 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1912 if (key.lsb < 127) {
1914 change_patch_change (patch, key);
1916 if (key.msb < 127) {
1919 change_patch_change (patch, key);
1926 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1928 if (_selection.empty()) {
1932 _selection.erase (cne);
1936 MidiRegionView::delete_selection()
1938 if (_selection.empty()) {
1942 start_note_diff_command (_("delete selection"));
1944 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1945 if ((*i)->selected()) {
1946 _note_diff_command->remove((*i)->note());
1956 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1958 start_note_diff_command (_("delete note"));
1959 _note_diff_command->remove (n);
1962 trackview.editor().verbose_cursor()->hide ();
1966 MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal)
1968 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1970 Selection::iterator tmp = i;
1973 (*i)->set_selected (false);
1974 (*i)->hide_velocity ();
1975 _selection.erase (i);
1983 /* this does not change the status of this regionview w.r.t the editor
1988 SelectionCleared (this); /* EMIT SIGNAL */
1993 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1995 clear_selection_except (ev);
1997 /* don't bother with checking to see if we should remove this
1998 regionview from the editor selection, since we're about to add
1999 another note, and thus put/keep this regionview in the editor
2003 if (!ev->selected()) {
2004 add_to_selection (ev);
2009 MidiRegionView::select_all_notes ()
2013 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2014 add_to_selection (*i);
2019 MidiRegionView::select_range (framepos_t start, framepos_t end)
2023 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2024 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2025 if (t >= start && t <= end) {
2026 add_to_selection (*i);
2032 MidiRegionView::invert_selection ()
2034 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2035 if ((*i)->selected()) {
2036 remove_from_selection(*i);
2038 add_to_selection (*i);
2044 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2046 uint8_t low_note = 127;
2047 uint8_t high_note = 0;
2048 MidiModel::Notes& notes (_model->notes());
2049 _optimization_iterator = _events.begin();
2055 if (extend && _selection.empty()) {
2061 /* scan existing selection to get note range */
2063 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2064 if ((*i)->note()->note() < low_note) {
2065 low_note = (*i)->note()->note();
2067 if ((*i)->note()->note() > high_note) {
2068 high_note = (*i)->note()->note();
2072 low_note = min (low_note, notenum);
2073 high_note = max (high_note, notenum);
2076 _no_sound_notes = true;
2078 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2080 boost::shared_ptr<NoteType> note (*n);
2081 CanvasNoteEvent* cne;
2082 bool select = false;
2084 if (((1 << note->channel()) & channel_mask) != 0) {
2086 if ((note->note() >= low_note && note->note() <= high_note)) {
2089 } else if (note->note() == notenum) {
2095 if ((cne = find_canvas_note (note)) != 0) {
2096 // extend is false because we've taken care of it,
2097 // since it extends by time range, not pitch.
2098 note_selected (cne, add, false);
2102 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2106 _no_sound_notes = false;
2110 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2112 MidiModel::Notes& notes (_model->notes());
2113 _optimization_iterator = _events.begin();
2115 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2117 boost::shared_ptr<NoteType> note (*n);
2118 CanvasNoteEvent* cne;
2120 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2121 if ((cne = find_canvas_note (note)) != 0) {
2122 if (cne->selected()) {
2123 note_deselected (cne);
2125 note_selected (cne, true, false);
2133 MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2136 clear_selection_except (ev);
2137 if (!_selection.empty()) {
2138 PublicEditor& editor (trackview.editor());
2139 editor.get_selection().add (this);
2145 if (!ev->selected()) {
2146 add_to_selection (ev);
2150 /* find end of latest note selected, select all between that and the start of "ev" */
2152 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2153 Evoral::MusicalTime latest = 0;
2155 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2156 if ((*i)->note()->end_time() > latest) {
2157 latest = (*i)->note()->end_time();
2159 if ((*i)->note()->time() < earliest) {
2160 earliest = (*i)->note()->time();
2164 if (ev->note()->end_time() > latest) {
2165 latest = ev->note()->end_time();
2168 if (ev->note()->time() < earliest) {
2169 earliest = ev->note()->time();
2172 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2174 /* find notes entirely within OR spanning the earliest..latest range */
2176 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2177 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2178 add_to_selection (*i);
2186 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2188 remove_from_selection (ev);
2192 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
2202 // TODO: Make this faster by storing the last updated selection rect, and only
2203 // adjusting things that are in the area that appears/disappeared.
2204 // We probably need a tree to be able to find events in O(log(n)) time.
2206 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2208 /* check if any corner of the note is inside the rect
2211 1) this is computing "touched by", not "contained by" the rect.
2212 2) this does not require that events be sorted in time.
2215 const double ix1 = (*i)->x1();
2216 const double ix2 = (*i)->x2();
2217 const double iy1 = (*i)->y1();
2218 const double iy2 = (*i)->y2();
2220 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2221 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2222 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2223 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2226 if (!(*i)->selected()) {
2227 add_to_selection (*i);
2229 } else if ((*i)->selected() && !extend) {
2230 // Not inside rectangle
2231 remove_from_selection (*i);
2237 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2239 Selection::iterator i = _selection.find (ev);
2241 if (i != _selection.end()) {
2242 _selection.erase (i);
2245 ev->set_selected (false);
2246 ev->hide_velocity ();
2248 if (_selection.empty()) {
2249 PublicEditor& editor (trackview.editor());
2250 editor.get_selection().remove (this);
2255 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2257 bool add_mrv_selection = false;
2259 if (_selection.empty()) {
2260 add_mrv_selection = true;
2263 if (_selection.insert (ev).second) {
2264 ev->set_selected (true);
2265 play_midi_note ((ev)->note());
2268 if (add_mrv_selection) {
2269 PublicEditor& editor (trackview.editor());
2270 editor.get_selection().add (this);
2275 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2277 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2278 PossibleChord to_play;
2279 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2281 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2282 if ((*i)->note()->time() < earliest) {
2283 earliest = (*i)->note()->time();
2287 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2288 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2289 to_play.push_back ((*i)->note());
2291 (*i)->move_event(dx, dy);
2294 if (dy && !_selection.empty() && !_no_sound_notes && trackview.editor().sound_notes()) {
2296 if (to_play.size() > 1) {
2298 PossibleChord shifted;
2300 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2301 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2302 moved_note->set_note (moved_note->note() + cumulative_dy);
2303 shifted.push_back (moved_note);
2306 play_midi_chord (shifted);
2308 } else if (!to_play.empty()) {
2310 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2311 moved_note->set_note (moved_note->note() + cumulative_dy);
2312 play_midi_note (moved_note);
2318 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2320 assert (!_selection.empty());
2322 uint8_t lowest_note_in_selection = 127;
2323 uint8_t highest_note_in_selection = 0;
2324 uint8_t highest_note_difference = 0;
2326 // find highest and lowest notes first
2328 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2329 uint8_t pitch = (*i)->note()->note();
2330 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2331 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2335 cerr << "dnote: " << (int) dnote << endl;
2336 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2337 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2338 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2339 << int(highest_note_in_selection) << endl;
2340 cerr << "selection size: " << _selection.size() << endl;
2341 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2344 // Make sure the note pitch does not exceed the MIDI standard range
2345 if (highest_note_in_selection + dnote > 127) {
2346 highest_note_difference = highest_note_in_selection - 127;
2349 start_note_diff_command (_("move notes"));
2351 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2353 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (source_beats_to_absolute_frames ((*i)->note()->time()) + dt);
2359 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2361 uint8_t original_pitch = (*i)->note()->note();
2362 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2364 // keep notes in standard midi range
2365 clamp_to_0_127(new_pitch);
2367 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2368 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2370 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2375 // care about notes being moved beyond the upper/lower bounds on the canvas
2376 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2377 highest_note_in_selection > midi_stream_view()->highest_note()) {
2378 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2382 /** @param x Pixel relative to the region position.
2383 * @return Snapped frame relative to the region position.
2386 MidiRegionView::snap_pixel_to_frame(double x)
2388 PublicEditor& editor (trackview.editor());
2389 return snap_frame_to_frame (editor.pixel_to_frame (x));
2392 /** @param x Pixel relative to the region position.
2393 * @return Snapped pixel relative to the region position.
2396 MidiRegionView::snap_to_pixel(double x)
2398 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2402 MidiRegionView::get_position_pixels()
2404 framepos_t region_frame = get_position();
2405 return trackview.editor().frame_to_pixel(region_frame);
2409 MidiRegionView::get_end_position_pixels()
2411 framepos_t frame = get_position() + get_duration ();
2412 return trackview.editor().frame_to_pixel(frame);
2416 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2418 /* the time converter will return the frame corresponding to `beats'
2419 relative to the start of the source. The start of the source
2420 is an implied position given by region->position - region->start
2422 const framepos_t source_start = _region->position() - _region->start();
2423 return source_start + _source_relative_time_converter.to (beats);
2427 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2429 /* the `frames' argument needs to be converted into a frame count
2430 relative to the start of the source before being passed in to the
2433 const framepos_t source_start = _region->position() - _region->start();
2434 return _source_relative_time_converter.from (frames - source_start);
2438 MidiRegionView::region_beats_to_region_frames(double beats) const
2440 return _region_relative_time_converter.to(beats);
2444 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2446 return _region_relative_time_converter.from(frames);
2450 MidiRegionView::begin_resizing (bool /*at_front*/)
2452 _resize_data.clear();
2454 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2455 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2457 // only insert CanvasNotes into the map
2459 NoteResizeData *resize_data = new NoteResizeData();
2460 resize_data->canvas_note = note;
2462 // create a new SimpleRect from the note which will be the resize preview
2463 SimpleRect *resize_rect = new SimpleRect(
2464 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2466 // calculate the colors: get the color settings
2467 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2468 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2471 // make the resize preview notes more transparent and bright
2472 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2474 // calculate color based on note velocity
2475 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2476 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2480 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2481 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2483 resize_data->resize_rect = resize_rect;
2484 _resize_data.push_back(resize_data);
2489 /** Update resizing notes while user drags.
2490 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2491 * @param at_front which end of the note (true == note on, false == note off)
2492 * @param delta_x change in mouse position since the start of the drag
2493 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2494 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2495 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2496 * as the \a primary note.
2499 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2501 bool cursor_set = false;
2503 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2504 SimpleRect* resize_rect = (*i)->resize_rect;
2505 CanvasNote* canvas_note = (*i)->canvas_note;
2510 current_x = canvas_note->x1() + delta_x;
2512 current_x = primary->x1() + delta_x;
2516 current_x = canvas_note->x2() + delta_x;
2518 current_x = primary->x2() + delta_x;
2523 resize_rect->property_x1() = snap_to_pixel(current_x);
2524 resize_rect->property_x2() = canvas_note->x2();
2526 resize_rect->property_x2() = snap_to_pixel(current_x);
2527 resize_rect->property_x1() = canvas_note->x1();
2533 beats = snap_pixel_to_frame (current_x);
2534 beats = region_frames_to_region_beats (beats);
2539 if (beats < canvas_note->note()->end_time()) {
2540 len = canvas_note->note()->time() - beats;
2541 len += canvas_note->note()->length();
2546 if (beats >= canvas_note->note()->time()) {
2547 len = beats - canvas_note->note()->time();
2554 snprintf (buf, sizeof (buf), "%.3g beats", len);
2555 show_verbose_cursor (buf, 0, 0);
2564 /** Finish resizing notes when the user releases the mouse button.
2565 * Parameters the same as for \a update_resizing().
2568 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2570 start_note_diff_command (_("resize notes"));
2572 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2573 CanvasNote* canvas_note = (*i)->canvas_note;
2574 SimpleRect* resize_rect = (*i)->resize_rect;
2576 /* Get the new x position for this resize, which is in pixels relative
2577 * to the region position.
2584 current_x = canvas_note->x1() + delta_x;
2586 current_x = primary->x1() + delta_x;
2590 current_x = canvas_note->x2() + delta_x;
2592 current_x = primary->x2() + delta_x;
2596 /* Convert that to a frame within the source */
2597 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2599 /* and then to beats */
2600 current_x = region_frames_to_region_beats (current_x);
2602 if (at_front && current_x < canvas_note->note()->end_time()) {
2603 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2605 double len = canvas_note->note()->time() - current_x;
2606 len += canvas_note->note()->length();
2609 /* XXX convert to beats */
2610 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2615 double len = current_x - canvas_note->note()->time();
2618 /* XXX convert to beats */
2619 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2627 _resize_data.clear();
2632 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2634 uint8_t new_velocity;
2637 new_velocity = event->note()->velocity() + velocity;
2638 clamp_to_0_127(new_velocity);
2640 new_velocity = velocity;
2643 event->set_selected (event->selected()); // change color
2645 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2649 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2654 new_note = event->note()->note() + note;
2659 clamp_to_0_127 (new_note);
2660 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2664 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2666 bool change_start = false;
2667 bool change_length = false;
2668 Evoral::MusicalTime new_start = 0;
2669 Evoral::MusicalTime new_length = 0;
2671 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2673 front_delta: if positive - move the start of the note later in time (shortening it)
2674 if negative - move the start of the note earlier in time (lengthening it)
2676 end_delta: if positive - move the end of the note later in time (lengthening it)
2677 if negative - move the end of the note earlier in time (shortening it)
2681 if (front_delta < 0) {
2683 if (event->note()->time() < -front_delta) {
2686 new_start = event->note()->time() + front_delta; // moves earlier
2689 /* start moved toward zero, so move the end point out to where it used to be.
2690 Note that front_delta is negative, so this increases the length.
2693 new_length = event->note()->length() - front_delta;
2694 change_start = true;
2695 change_length = true;
2699 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2701 if (new_pos < event->note()->end_time()) {
2702 new_start = event->note()->time() + front_delta;
2703 /* start moved toward the end, so move the end point back to where it used to be */
2704 new_length = event->note()->length() - front_delta;
2705 change_start = true;
2706 change_length = true;
2713 bool can_change = true;
2714 if (end_delta < 0) {
2715 if (event->note()->length() < -end_delta) {
2721 new_length = event->note()->length() + end_delta;
2722 change_length = true;
2727 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2730 if (change_length) {
2731 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2736 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2738 uint8_t new_channel;
2742 if (event->note()->channel() < -chn) {
2745 new_channel = event->note()->channel() + chn;
2748 new_channel = event->note()->channel() + chn;
2751 new_channel = (uint8_t) chn;
2754 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2758 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2760 Evoral::MusicalTime new_time;
2764 if (event->note()->time() < -delta) {
2767 new_time = event->note()->time() + delta;
2770 new_time = event->note()->time() + delta;
2776 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2780 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2782 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2786 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2790 if (_selection.empty()) {
2805 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2806 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2812 start_note_diff_command (_("change velocities"));
2814 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2815 Selection::iterator next = i;
2817 change_note_velocity (*i, delta, true);
2823 if (!_selection.empty()) {
2825 snprintf (buf, sizeof (buf), "Vel %d",
2826 (int) (*_selection.begin())->note()->velocity());
2827 show_verbose_cursor (buf, 10, 10);
2833 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2835 if (_selection.empty()) {
2852 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2854 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2858 if ((int8_t) (*i)->note()->note() + delta > 127) {
2865 start_note_diff_command (_("transpose"));
2867 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2868 Selection::iterator next = i;
2870 change_note_note (*i, delta, true);
2878 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2884 /* grab the current grid distance */
2886 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2888 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2889 cerr << "Grid type not available as beats - TO BE FIXED\n";
2899 start_note_diff_command (_("change note lengths"));
2901 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2902 Selection::iterator next = i;
2905 /* note the negation of the delta for start */
2907 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2916 MidiRegionView::nudge_notes (bool forward)
2918 if (_selection.empty()) {
2922 /* pick a note as the point along the timeline to get the nudge distance.
2923 its not necessarily the earliest note, so we may want to pull the notes out
2924 into a vector and sort before using the first one.
2927 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
2929 framepos_t distance;
2931 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2933 /* grid is off - use nudge distance */
2935 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2941 framepos_t next_pos = ref_point;
2944 if (max_framepos - 1 < next_pos) {
2948 if (next_pos == 0) {
2954 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2955 distance = ref_point - next_pos;
2958 if (distance == 0) {
2962 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
2968 start_note_diff_command (_("nudge"));
2970 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2971 Selection::iterator next = i;
2973 change_note_time (*i, delta, true);
2981 MidiRegionView::change_channel(uint8_t channel)
2983 start_note_diff_command(_("change channel"));
2984 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2985 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2993 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2995 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2997 _pre_enter_cursor = editor->get_canvas_cursor ();
2999 if (_mouse_state == SelectTouchDragging) {
3000 note_selected (ev, true);
3003 show_verbose_cursor (ev->note ());
3007 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
3009 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3011 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3012 (*i)->hide_velocity ();
3015 editor->verbose_cursor()->hide ();
3017 if (_pre_enter_cursor) {
3018 editor->set_canvas_cursor (_pre_enter_cursor);
3019 _pre_enter_cursor = 0;
3024 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
3027 /* XXX should get patch name if we can */
3028 s << _("Bank:") << (ev->patch()->bank() + MIDI_BP_ZERO) << '\n' << _("Program:") << ((int) ev->patch()->program()) + MIDI_BP_ZERO << '\n' << _("Channel:") << ((int) ev->patch()->channel() + 1);
3029 show_verbose_cursor (s.str(), 10, 20);
3033 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
3035 trackview.editor().verbose_cursor()->hide ();
3039 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3041 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3043 if (x_fraction > 0.0 && x_fraction < 0.25) {
3044 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3045 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
3046 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3048 if (_pre_enter_cursor && can_set_cursor) {
3049 editor->set_canvas_cursor (_pre_enter_cursor);
3055 MidiRegionView::set_frame_color()
3059 TimeAxisViewItem::set_frame_color ();
3066 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3067 } else if (high_enough_for_name) {
3068 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3073 if (!rect_visible) {
3074 f = UINT_RGBA_CHANGE_A (f, 0);
3077 frame->property_fill_color_rgba() = f;
3081 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3085 case FilterChannels:
3086 _force_channel = -1;
3089 _force_channel = mask;
3090 mask = 0xFFFF; // Show all notes as active (below)
3093 // Update notes for selection
3094 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3095 (*i)->on_channel_selection_change(mask);
3098 _last_channel_selection = mask;
3100 _patch_changes.clear ();
3101 display_patch_changes ();
3105 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
3107 _model_name = model;
3108 _custom_device_mode = custom_device_mode;
3113 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3115 if (_selection.empty()) {
3119 PublicEditor& editor (trackview.editor());
3123 /* XXX what to do ? */
3127 editor.get_cut_buffer().add (selection_as_cut_buffer());
3135 start_note_diff_command();
3137 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3144 note_diff_remove_note (*i);
3154 MidiRegionView::selection_as_cut_buffer () const
3158 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3159 NoteType* n = (*i)->note().get();
3160 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3163 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3169 /** This method handles undo */
3171 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3177 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3179 trackview.session()->begin_reversible_command (_("paste"));
3181 start_note_diff_command (_("paste"));
3183 Evoral::MusicalTime beat_delta;
3184 Evoral::MusicalTime paste_pos_beats;
3185 Evoral::MusicalTime duration;
3186 Evoral::MusicalTime end_point = 0;
3188 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3189 paste_pos_beats = region_frames_to_region_beats (pos - _region->position());
3190 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3191 paste_pos_beats = 0;
3193 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",
3194 (*mcb.notes().begin())->time(),
3195 (*mcb.notes().rbegin())->end_time(),
3196 duration, pos, _region->position(),
3197 paste_pos_beats, beat_delta));
3201 for (int n = 0; n < (int) times; ++n) {
3203 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3205 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3206 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3208 /* make all newly added notes selected */
3210 note_diff_add_note (copied_note, true);
3211 end_point = copied_note->end_time();
3214 paste_pos_beats += duration;
3217 /* if we pasted past the current end of the region, extend the region */
3219 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3220 framepos_t region_end = _region->position() + _region->length() - 1;
3222 if (end_frame > region_end) {
3224 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3226 _region->clear_changes ();
3227 _region->set_length (end_frame);
3228 trackview.session()->add_command (new StatefulDiffCommand (_region));
3233 trackview.session()->commit_reversible_command ();
3236 struct EventNoteTimeEarlyFirstComparator {
3237 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3238 return a->note()->time() < b->note()->time();
3243 MidiRegionView::time_sort_events ()
3245 if (!_sort_needed) {
3249 EventNoteTimeEarlyFirstComparator cmp;
3252 _sort_needed = false;
3256 MidiRegionView::goto_next_note (bool add_to_selection)
3258 bool use_next = false;
3260 if (_events.back()->selected()) {
3264 time_sort_events ();
3266 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3267 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3269 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3270 if ((*i)->selected()) {
3273 } else if (use_next) {
3274 if (channel_mask & (1 << (*i)->note()->channel())) {
3275 if (!add_to_selection) {
3278 note_selected (*i, true, false);
3285 /* use the first one */
3287 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3288 unique_select (_events.front());
3293 MidiRegionView::goto_previous_note (bool add_to_selection)
3295 bool use_next = false;
3297 if (_events.front()->selected()) {
3301 time_sort_events ();
3303 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3304 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3306 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3307 if ((*i)->selected()) {
3310 } else if (use_next) {
3311 if (channel_mask & (1 << (*i)->note()->channel())) {
3312 if (!add_to_selection) {
3315 note_selected (*i, true, false);
3322 /* use the last one */
3324 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3325 unique_select (*(_events.rbegin()));
3330 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3332 bool had_selected = false;
3334 time_sort_events ();
3336 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3337 if ((*i)->selected()) {
3338 selected.insert ((*i)->note());
3339 had_selected = true;
3343 if (allow_all_if_none_selected && !had_selected) {
3344 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3345 selected.insert ((*i)->note());
3351 MidiRegionView::update_ghost_note (double x, double y)
3353 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3358 _note_group->w2i (x, y);
3360 PublicEditor& editor = trackview.editor ();
3362 framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3363 framecnt_t grid_frames;
3364 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3366 /* use region_frames... because we are converting a delta within the region
3369 double length = region_frames_to_region_beats (snap_frame_to_frame (f + grid_frames) - f);
3371 /* note that this sets the time of the ghost note in beats relative to
3372 the start of the source; that is how all note times are stored.
3374 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3375 _ghost_note->note()->set_length (length);
3376 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3377 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3379 /* the ghost note does not appear in ghost regions, so pass false in here */
3380 update_note (_ghost_note, false);
3382 show_verbose_cursor (_ghost_note->note ());
3386 MidiRegionView::create_ghost_note (double x, double y)
3391 boost::shared_ptr<NoteType> g (new NoteType);
3392 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3393 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3394 update_ghost_note (x, y);
3395 _ghost_note->show ();
3400 show_verbose_cursor (_ghost_note->note ());
3404 MidiRegionView::snap_changed ()
3410 create_ghost_note (_last_ghost_x, _last_ghost_y);
3414 MidiRegionView::drop_down_keys ()
3416 _mouse_state = None;
3420 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3422 double note = midi_stream_view()->y_to_note(y);
3424 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3426 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3428 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3429 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3430 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3431 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3436 bool add_mrv_selection = false;
3438 if (_selection.empty()) {
3439 add_mrv_selection = true;
3442 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3443 if (_selection.insert (*i).second) {
3444 (*i)->set_selected (true);
3448 if (add_mrv_selection) {
3449 PublicEditor& editor (trackview.editor());
3450 editor.get_selection().add (this);
3455 MidiRegionView::color_handler ()
3457 RegionView::color_handler ();
3459 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3460 (*i)->set_selected ((*i)->selected()); // will change color
3463 /* XXX probably more to do here */
3467 MidiRegionView::enable_display (bool yn)
3469 RegionView::enable_display (yn);
3476 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3478 if (_step_edit_cursor == 0) {
3479 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3481 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3482 _step_edit_cursor->property_y1() = 0;
3483 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3484 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3485 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3488 move_step_edit_cursor (pos);
3489 _step_edit_cursor->show ();
3493 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3495 _step_edit_cursor_position = pos;
3497 if (_step_edit_cursor) {
3498 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3499 _step_edit_cursor->property_x1() = pixel;
3500 set_step_edit_cursor_width (_step_edit_cursor_width);
3505 MidiRegionView::hide_step_edit_cursor ()
3507 if (_step_edit_cursor) {
3508 _step_edit_cursor->hide ();
3513 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3515 _step_edit_cursor_width = beats;
3517 if (_step_edit_cursor) {
3518 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3522 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3523 * @param w Source that the data will end up in.
3526 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3528 if (!_active_notes) {
3529 /* we aren't actively being recorded to */
3533 boost::shared_ptr<MidiSource> src = w.lock ();
3534 if (!src || src != midi_region()->midi_source()) {
3535 /* recorded data was not destined for our source */
3539 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3541 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3543 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3545 framepos_t back = max_framepos;
3547 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3548 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3549 assert (ev.buffer ());
3551 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3553 if (ev.type() == MIDI_CMD_NOTE_ON) {
3555 boost::shared_ptr<NoteType> note (
3556 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3559 add_note (note, true);
3561 /* fix up our note range */
3562 if (ev.note() < _current_range_min) {
3563 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3564 } else if (ev.note() > _current_range_max) {
3565 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3568 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3569 resolve_note (ev.note (), time_beats);
3575 midi_stream_view()->check_record_layers (region(), back);
3579 MidiRegionView::trim_front_starting ()
3581 /* Reparent the note group to the region view's parent, so that it doesn't change
3582 when the region view is trimmed.
3584 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3585 _temporary_note_group->move (group->property_x(), group->property_y());
3586 _note_group->reparent (*_temporary_note_group);
3590 MidiRegionView::trim_front_ending ()
3592 _note_group->reparent (*group);
3593 delete _temporary_note_group;
3594 _temporary_note_group = 0;
3596 if (_region->start() < 0) {
3597 /* Trim drag made start time -ve; fix this */
3598 midi_region()->fix_negative_start ();
3603 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3605 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3606 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3610 change_patch_change (pc->patch(), d.patch ());
3615 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3618 snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3619 Evoral::midi_note_name (n->note()).c_str(),
3621 (int) n->channel() + 1,
3622 (int) n->velocity());
3624 show_verbose_cursor (buf, 10, 20);
3628 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3632 trackview.editor().get_pointer_position (wx, wy);
3637 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3639 double x1, y1, x2, y2;
3640 trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3642 if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3643 wy -= (y2 - y1) + 2 * yoffset;
3646 trackview.editor().verbose_cursor()->set (text, wx, wy);
3647 trackview.editor().verbose_cursor()->show ();
3650 /** @param p A session framepos.
3651 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3652 * @return p snapped to the grid subdivision underneath it.
3655 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3657 PublicEditor& editor = trackview.editor ();
3660 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3666 grid_frames = region_beats_to_region_frames (grid_beats);
3668 /* Hack so that we always snap to the note that we are over, instead of snapping
3669 to the next one if we're more than halfway through the one we're over.
3671 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3672 p -= grid_frames / 2;
3675 return snap_frame_to_frame (p);
3678 /** Called when the selection has been cleared in any MidiRegionView.
3679 * @param rv MidiRegionView that the selection was cleared in.
3682 MidiRegionView::selection_cleared (MidiRegionView* rv)
3688 /* Clear our selection in sympathy; but don't signal the fact */
3689 clear_selection (false);