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 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
85 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
86 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
87 : RegionView (parent, tv, r, spu, basic_color)
89 , _last_channel_selection(0xFFFF)
90 , _current_range_min(0)
91 , _current_range_max(0)
92 , _model_name(string())
93 , _custom_device_mode(string())
95 , _note_group(new ArdourCanvas::Group(*group))
96 , _note_diff_command (0)
99 , _step_edit_cursor (0)
100 , _step_edit_cursor_width (1.0)
101 , _step_edit_cursor_position (0.0)
102 , _channel_selection_scoped_note (0)
103 , _temporary_note_group (0)
106 , _sort_needed (true)
107 , _optimization_iterator (_events.end())
109 , _no_sound_notes (false)
112 , _pre_enter_cursor (0)
114 _note_group->raise_to_top();
115 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
117 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
118 connect_to_diskstream ();
121 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
122 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
123 TimeAxisViewItem::Visibility visibility)
124 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
126 , _last_channel_selection(0xFFFF)
127 , _model_name(string())
128 , _custom_device_mode(string())
130 , _note_group(new ArdourCanvas::Group(*parent))
131 , _note_diff_command (0)
134 , _step_edit_cursor (0)
135 , _step_edit_cursor_width (1.0)
136 , _step_edit_cursor_position (0.0)
137 , _channel_selection_scoped_note (0)
138 , _temporary_note_group (0)
141 , _sort_needed (true)
142 , _optimization_iterator (_events.end())
144 , _no_sound_notes (false)
148 _note_group->raise_to_top();
149 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
151 connect_to_diskstream ();
155 MidiRegionView::parameter_changed (std::string const & p)
157 if (p == "diplay-first-midi-bank-as-zero") {
158 if (_enable_display) {
164 MidiRegionView::MidiRegionView (const MidiRegionView& other)
165 : sigc::trackable(other)
168 , _last_channel_selection(0xFFFF)
169 , _model_name(string())
170 , _custom_device_mode(string())
172 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
173 , _note_diff_command (0)
176 , _step_edit_cursor (0)
177 , _step_edit_cursor_width (1.0)
178 , _step_edit_cursor_position (0.0)
179 , _channel_selection_scoped_note (0)
180 , _temporary_note_group (0)
183 , _sort_needed (true)
184 , _optimization_iterator (_events.end())
186 , _no_sound_notes (false)
193 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
194 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
199 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
200 : RegionView (other, boost::shared_ptr<Region> (region))
202 , _last_channel_selection(0xFFFF)
203 , _model_name(string())
204 , _custom_device_mode(string())
206 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
207 , _note_diff_command (0)
210 , _step_edit_cursor (0)
211 , _step_edit_cursor_width (1.0)
212 , _step_edit_cursor_position (0.0)
213 , _channel_selection_scoped_note (0)
214 , _temporary_note_group (0)
217 , _sort_needed (true)
218 , _optimization_iterator (_events.end())
220 , _no_sound_notes (false)
227 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
228 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
234 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
236 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
238 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
239 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
243 midi_region()->midi_source(0)->load_model();
246 _model = midi_region()->midi_source(0)->model();
247 _enable_display = false;
249 RegionView::init (basic_color, false);
251 compute_colors (basic_color);
253 set_height (trackview.current_height());
256 region_sync_changed ();
257 region_resized (ARDOUR::bounds_change);
260 reset_width_dependent_items (_pixel_width);
264 _enable_display = true;
267 display_model (_model);
271 group->raise_to_top();
272 group->signal_event().connect(
273 sigc::mem_fun(this, &MidiRegionView::canvas_event), false);
275 midi_view()->signal_channel_mode_changed().connect(
276 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
278 midi_view()->signal_midi_patch_settings_changed().connect(
279 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
281 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
282 ui_bind(&MidiRegionView::snap_changed, this),
285 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
286 connect_to_diskstream ();
290 MidiRegionView::connect_to_diskstream ()
292 midi_view()->midi_track()->DataRecorded.connect(
293 *this, invalidator(*this),
294 ui_bind(&MidiRegionView::data_recorded, this, _1, _2),
299 MidiRegionView::canvas_event(GdkEvent* ev)
302 case GDK_ENTER_NOTIFY:
303 case GDK_LEAVE_NOTIFY:
304 _last_event_x = ev->crossing.x;
305 _last_event_y = ev->crossing.y;
307 case GDK_MOTION_NOTIFY:
308 _last_event_x = ev->motion.x;
309 _last_event_y = ev->motion.y;
315 if (!trackview.editor().internal_editing()) {
321 return scroll (&ev->scroll);
324 return key_press (&ev->key);
326 case GDK_KEY_RELEASE:
327 return key_release (&ev->key);
329 case GDK_BUTTON_PRESS:
330 return button_press (&ev->button);
332 case GDK_2BUTTON_PRESS:
335 case GDK_BUTTON_RELEASE:
336 return button_release (&ev->button);
338 case GDK_ENTER_NOTIFY:
339 return enter_notify (&ev->crossing);
341 case GDK_LEAVE_NOTIFY:
342 return leave_notify (&ev->crossing);
344 case GDK_MOTION_NOTIFY:
345 return motion (&ev->motion);
355 MidiRegionView::remove_ghost_note ()
362 MidiRegionView::enter_notify (GdkEventCrossing* ev)
364 trackview.editor().MouseModeChanged.connect (
365 _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
368 if (trackview.editor().current_mouse_mode() == MouseRange) {
369 create_ghost_note (ev->x, ev->y);
372 if (!trackview.editor().internal_editing()) {
373 Keyboard::magic_widget_drop_focus();
375 Keyboard::magic_widget_grab_focus();
383 MidiRegionView::leave_notify (GdkEventCrossing* ev)
385 _mouse_mode_connection.disconnect ();
387 trackview.editor().verbose_cursor()->hide ();
388 remove_ghost_note ();
394 MidiRegionView::mouse_mode_changed ()
396 if (trackview.editor().current_mouse_mode() == MouseRange && trackview.editor().internal_editing()) {
397 create_ghost_note (_last_event_x, _last_event_y);
399 remove_ghost_note ();
400 trackview.editor().verbose_cursor()->hide ();
403 if (!trackview.editor().internal_editing()) {
404 Keyboard::magic_widget_drop_focus();
406 Keyboard::magic_widget_grab_focus();
412 MidiRegionView::button_press (GdkEventButton* ev)
414 if (ev->button != 1) {
421 group->w2i (_last_x, _last_y);
423 if (_mouse_state != SelectTouchDragging) {
425 _pressed_button = ev->button;
426 _mouse_state = Pressed;
431 _pressed_button = ev->button;
437 MidiRegionView::button_release (GdkEventButton* ev)
439 double event_x, event_y;
441 if (ev->button != 1) {
448 group->w2i(event_x, event_y);
449 group->ungrab(ev->time);
451 switch (_mouse_state) {
452 case Pressed: // Clicked
454 switch (trackview.editor().current_mouse_mode()) {
460 if (Keyboard::is_insert_note_event(ev)) {
462 double event_x, event_y;
466 group->w2i(event_x, event_y);
469 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
475 create_note_at (event_x, event_y, beats, true, true);
483 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
489 create_note_at (event_x, event_y, beats, true, true);
500 case SelectRectDragging: // Select drag done
507 case AddDragging: // Add drag done
511 if (Keyboard::is_insert_note_event(ev) || trackview.editor().current_mouse_mode() == MouseRange) {
513 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
515 const double x = _drag_rect->property_x1();
516 const double length = trackview.editor().pixel_to_frame (_drag_rect->property_x2() - _drag_rect->property_x1());
518 create_note_at (x, _drag_rect->property_y1(), region_frames_to_region_beats(length), true, false);
525 create_ghost_note (ev->x, ev->y);
535 MidiRegionView::motion (GdkEventMotion* ev)
537 double event_x, event_y;
538 framepos_t event_frame = 0;
542 group->w2i(event_x, event_y);
544 PublicEditor& editor = trackview.editor ();
546 // convert event_x to global frame
547 framecnt_t grid_frames;
548 event_frame = snap_frame_to_grid_underneath (editor.pixel_to_frame (event_x), grid_frames);
550 if (!_ghost_note && editor.current_mouse_mode() != MouseRange
551 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())
552 && _mouse_state != AddDragging) {
554 create_ghost_note (ev->x, ev->y);
555 } else if (_ghost_note && editor.current_mouse_mode() != MouseRange
556 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
558 update_ghost_note (ev->x, ev->y);
559 } else if (_ghost_note && editor.current_mouse_mode() != MouseRange) {
564 editor.verbose_cursor()->hide ();
565 } else if (_ghost_note && editor.current_mouse_mode() == MouseRange) {
566 update_ghost_note (ev->x, ev->y);
569 /* any motion immediately hides velocity text that may have been visible */
571 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
572 (*i)->hide_velocity ();
575 switch (_mouse_state) {
576 case Pressed: // Maybe start a drag, if we've moved a bit
578 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
579 /* no appreciable movement since the button was pressed */
583 if (_pressed_button == 1 && editor.current_mouse_mode() == MouseObject
584 && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
587 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
588 Gdk::Cursor(Gdk::FLEUR), ev->time);
592 _drag_start_x = event_x;
593 _drag_start_y = event_y;
595 _drag_rect = new ArdourCanvas::SimpleRect(*group);
596 _drag_rect->property_x1() = event_x;
597 _drag_rect->property_y1() = event_y;
598 _drag_rect->property_x2() = event_x;
599 _drag_rect->property_y2() = event_y;
600 _drag_rect->property_outline_what() = 0xFF;
601 _drag_rect->property_outline_color_rgba()
602 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
603 _drag_rect->property_fill_color_rgba()
604 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
606 _mouse_state = SelectRectDragging;
609 } else if (editor.internal_editing()) {
610 // Add note drag start
612 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
613 Gdk::Cursor(Gdk::FLEUR), ev->time);
617 _drag_start_x = event_x;
618 _drag_start_y = event_y;
620 _drag_rect = new ArdourCanvas::SimpleRect(*group);
621 _drag_rect->property_x1() = editor.frame_to_pixel(event_frame);
623 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
624 midi_stream_view()->y_to_note(event_y));
625 _drag_rect->property_x2() = editor.frame_to_pixel(event_frame);
627 _drag_rect->property_y2() = _drag_rect->property_y1()
628 + floor(midi_stream_view()->note_height());
629 _drag_rect->property_outline_what() = 0xFF;
630 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
631 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
633 _mouse_state = AddDragging;
638 editor.verbose_cursor()->hide ();
645 case SelectRectDragging: // Select drag motion
646 case AddDragging: // Add note drag motion
651 GdkModifierType state;
652 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
657 if (_mouse_state == AddDragging) {
658 event_x = editor.frame_to_pixel(event_frame);
660 if (editor.snap_mode() == SnapNormal) {
661 /* event_frame will have been snapped to the start of the note we are under;
662 it's more intuitive if we use the end of that note here
664 event_x = editor.frame_to_pixel (event_frame + grid_frames);
666 event_x = editor.frame_to_pixel (event_frame);
673 if (event_x > _drag_start_x) {
674 _drag_rect->property_x2() = event_x;
677 _drag_rect->property_x1() = event_x;
681 if (_drag_rect && _mouse_state == SelectRectDragging) {
683 if (event_y > _drag_start_y) {
684 _drag_rect->property_y2() = event_y;
687 _drag_rect->property_y1() = event_y;
690 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y, Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
696 case SelectTouchDragging:
708 MidiRegionView::scroll (GdkEventScroll* ev)
710 if (_selection.empty()) {
714 trackview.editor().verbose_cursor()->hide ();
716 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
718 if (ev->direction == GDK_SCROLL_UP) {
719 change_velocities (true, fine, false);
720 } else if (ev->direction == GDK_SCROLL_DOWN) {
721 change_velocities (false, fine, false);
727 MidiRegionView::key_press (GdkEventKey* ev)
729 /* since GTK bindings are generally activated on press, and since
730 detectable auto-repeat is the name of the game and only sends
731 repeated presses, carry out key actions at key press, not release.
734 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
736 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
737 _mouse_state = SelectTouchDragging;
740 } else if (ev->keyval == GDK_Escape && unmodified) {
744 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
746 bool start = (ev->keyval == GDK_comma);
747 bool end = (ev->keyval == GDK_period);
748 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
749 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
751 change_note_lengths (fine, shorter, 0.0, start, end);
755 } else if (ev->keyval == GDK_Delete && unmodified) {
760 } else if (ev->keyval == GDK_Tab) {
762 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
763 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
765 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
769 } else if (ev->keyval == GDK_ISO_Left_Tab) {
771 /* Shift-TAB generates ISO Left Tab, for some reason */
773 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
774 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
776 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
782 } else if (ev->keyval == GDK_Up) {
784 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
785 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
787 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
788 change_velocities (true, fine, allow_smush);
790 transpose (true, fine, allow_smush);
794 } else if (ev->keyval == GDK_Down) {
796 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
797 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
799 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
800 change_velocities (false, fine, allow_smush);
802 transpose (false, fine, allow_smush);
806 } else if (ev->keyval == GDK_Left && unmodified) {
811 } else if (ev->keyval == GDK_Right && unmodified) {
816 } else if (ev->keyval == GDK_c && unmodified) {
825 MidiRegionView::key_release (GdkEventKey* ev)
827 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
835 MidiRegionView::channel_edit ()
838 uint8_t current_channel;
840 if (_selection.empty()) {
844 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
846 current_channel = (*i)->note()->channel ();
851 MidiChannelDialog channel_dialog (current_channel);
852 int ret = channel_dialog.run ();
855 case Gtk::RESPONSE_OK:
861 uint8_t new_channel = channel_dialog.active_channel ();
863 start_note_diff_command (_("channel edit"));
865 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
866 Selection::iterator next = i;
868 change_note_channel (*i, new_channel);
876 MidiRegionView::show_list_editor ()
879 _list_editor = new MidiListEditor (trackview.session(), midi_region());
881 _list_editor->present ();
884 /** Add a note to the model, and the view, at a canvas (click) coordinate.
885 * \param x horizontal position in pixels
886 * \param y vertical position in pixels
887 * \param length duration of the note in beats, which will be snapped to the grid
888 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
889 * \param snap_x true to snap x to the grid, otherwise false.
892 MidiRegionView::create_note_at (double x, double y, double length, bool sh, bool snap_x)
894 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
895 MidiStreamView* const view = mtv->midi_view();
897 double note = view->y_to_note(y);
900 assert(note <= 127.0);
902 // Start of note in frames relative to region start
903 framepos_t start_frames = trackview.editor().pixel_to_frame (x);
905 framecnt_t grid_frames;
906 start_frames = snap_frame_to_grid_underneath (start_frames, grid_frames);
908 assert(start_frames >= 0);
911 length = region_frames_to_region_beats(
912 snap_frame_to_frame (start_frames + region_beats_to_region_frames(length)) - start_frames);
914 assert (length != 0);
917 length = region_frames_to_region_beats (region_beats_to_region_frames (length) - 1);
920 const boost::shared_ptr<NoteType> new_note (new NoteType (mtv->get_channel_for_add (),
921 region_frames_to_region_beats(start_frames + _region->start()), length,
922 (uint8_t)note, 0x40));
924 if (_model->contains (new_note)) {
928 view->update_note_range(new_note->note());
930 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note");
932 _model->apply_command(*trackview.session(), cmd);
934 play_midi_note (new_note);
938 MidiRegionView::clear_events()
943 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
944 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
949 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
954 _patch_changes.clear();
956 _optimization_iterator = _events.end();
960 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
964 content_connection.disconnect ();
965 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
969 if (_enable_display) {
975 MidiRegionView::start_note_diff_command (string name)
977 if (!_note_diff_command) {
978 _note_diff_command = _model->new_note_diff_command (name);
983 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
985 if (_note_diff_command) {
986 _note_diff_command->add (note);
989 _marked_for_selection.insert(note);
992 _marked_for_velocity.insert(note);
997 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
999 if (_note_diff_command && ev->note()) {
1000 _note_diff_command->remove(ev->note());
1005 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1006 MidiModel::NoteDiffCommand::Property property,
1009 if (_note_diff_command) {
1010 _note_diff_command->change (ev->note(), property, val);
1015 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1016 MidiModel::NoteDiffCommand::Property property,
1017 Evoral::MusicalTime val)
1019 if (_note_diff_command) {
1020 _note_diff_command->change (ev->note(), property, val);
1025 MidiRegionView::apply_diff (bool as_subcommand)
1029 if (!_note_diff_command) {
1033 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1034 // Mark all selected notes for selection when model reloads
1035 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1036 _marked_for_selection.insert((*i)->note());
1040 if (as_subcommand) {
1041 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1043 _model->apply_command (*trackview.session(), _note_diff_command);
1046 _note_diff_command = 0;
1047 midi_view()->midi_track()->playlist_modified();
1049 if (add_or_remove) {
1050 _marked_for_selection.clear();
1053 _marked_for_velocity.clear();
1057 MidiRegionView::abort_command()
1059 delete _note_diff_command;
1060 _note_diff_command = 0;
1065 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1067 if (_optimization_iterator != _events.end()) {
1068 ++_optimization_iterator;
1071 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1072 return *_optimization_iterator;
1075 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1076 if ((*_optimization_iterator)->note() == note) {
1077 return *_optimization_iterator;
1085 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1087 MidiModel::Notes notes;
1088 _model->get_notes (notes, op, val, chan_mask);
1090 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1091 CanvasNoteEvent* cne = find_canvas_note (*n);
1099 MidiRegionView::redisplay_model()
1101 // Don't redisplay the model if we're currently recording and displaying that
1102 if (_active_notes) {
1110 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1111 (*i)->invalidate ();
1114 MidiModel::ReadLock lock(_model->read_lock());
1116 MidiModel::Notes& notes (_model->notes());
1117 _optimization_iterator = _events.begin();
1119 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1121 boost::shared_ptr<NoteType> note (*n);
1122 CanvasNoteEvent* cne;
1125 if (note_in_region_range (note, visible)) {
1127 if ((cne = find_canvas_note (note)) != 0) {
1134 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1136 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1148 add_note (note, visible);
1153 if ((cne = find_canvas_note (note)) != 0) {
1161 /* remove note items that are no longer valid */
1163 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1164 if (!(*i)->valid ()) {
1166 i = _events.erase (i);
1172 _patch_changes.clear();
1176 display_patch_changes ();
1178 _marked_for_selection.clear ();
1179 _marked_for_velocity.clear ();
1181 /* we may have caused _events to contain things out of order (e.g. if a note
1182 moved earlier or later). we don't generally need them in time order, but
1183 make a note that a sort is required for those cases that require it.
1186 _sort_needed = true;
1190 MidiRegionView::display_patch_changes ()
1192 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1193 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1195 for (uint8_t i = 0; i < 16; ++i) {
1196 if (chn_mask & (1<<i)) {
1197 display_patch_changes_on_channel (i);
1199 /* TODO gray-out patch instad of not displaying it */
1204 MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
1206 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1208 if ((*i)->channel() != channel) {
1212 MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
1214 boost::shared_ptr<MIDI::Name::Patch> patch =
1215 MIDI::Name::MidiPatchManager::instance().find_patch(
1216 _model_name, _custom_device_mode, channel, patch_key);
1219 add_canvas_patch_change (*i, patch->name());
1222 /* program and bank numbers are zero-based: convert to one-based: MIDI_BP_ZERO */
1223 snprintf (buf, 16, "%d %d", (*i)->program() + MIDI_BP_ZERO , (*i)->bank() + MIDI_BP_ZERO);
1224 add_canvas_patch_change (*i, buf);
1230 MidiRegionView::display_sysexes()
1232 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1233 Evoral::MusicalTime time = (*i)->time();
1238 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1239 str << int((*i)->buffer()[b]);
1240 if (b != (*i)->size() -1) {
1244 string text = str.str();
1246 const double x = trackview.editor().frame_to_pixel(source_beats_to_absolute_frames(time));
1248 double height = midi_stream_view()->contents_height();
1250 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1251 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1253 // Show unless patch change is beyond the region bounds
1254 if (time - _region->start() >= _region->length() || time < _region->start()) {
1260 _sys_exes.push_back(sysex);
1265 MidiRegionView::~MidiRegionView ()
1267 in_destructor = true;
1269 trackview.editor().verbose_cursor()->hide ();
1271 note_delete_connection.disconnect ();
1273 delete _list_editor;
1275 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1277 if (_active_notes) {
1285 delete _note_diff_command;
1286 delete _step_edit_cursor;
1287 delete _temporary_note_group;
1291 MidiRegionView::region_resized (const PropertyChange& what_changed)
1293 RegionView::region_resized(what_changed);
1295 if (what_changed.contains (ARDOUR::Properties::position)) {
1296 set_duration(_region->length(), 0);
1297 if (_enable_display) {
1304 MidiRegionView::reset_width_dependent_items (double pixel_width)
1306 RegionView::reset_width_dependent_items(pixel_width);
1307 assert(_pixel_width == pixel_width);
1309 if (_enable_display) {
1313 move_step_edit_cursor (_step_edit_cursor_position);
1314 set_step_edit_cursor_width (_step_edit_cursor_width);
1318 MidiRegionView::set_height (double height)
1320 static const double FUDGE = 2.0;
1321 const double old_height = _height;
1322 RegionView::set_height(height);
1323 _height = height - FUDGE;
1325 apply_note_range(midi_stream_view()->lowest_note(),
1326 midi_stream_view()->highest_note(),
1327 height != old_height + FUDGE);
1330 name_pixbuf->raise_to_top();
1333 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1334 (*x)->set_height (midi_stream_view()->contents_height());
1337 if (_step_edit_cursor) {
1338 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1343 /** Apply the current note range from the stream view
1344 * by repositioning/hiding notes as necessary
1347 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1349 if (!_enable_display) {
1353 if (!force && _current_range_min == min && _current_range_max == max) {
1357 _current_range_min = min;
1358 _current_range_max = max;
1360 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1361 CanvasNoteEvent* event = *i;
1362 boost::shared_ptr<NoteType> note (event->note());
1364 if (note->note() < _current_range_min ||
1365 note->note() > _current_range_max) {
1371 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1373 const double y1 = midi_stream_view()->note_to_y(note->note());
1374 const double y2 = y1 + floor(midi_stream_view()->note_height());
1376 cnote->property_y1() = y1;
1377 cnote->property_y2() = y2;
1379 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1381 const double diamond_size = update_hit (chit);
1383 chit->set_height (diamond_size);
1389 MidiRegionView::add_ghost (TimeAxisView& tv)
1393 double unit_position = _region->position () / samples_per_unit;
1394 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1395 MidiGhostRegion* ghost;
1397 if (mtv && mtv->midi_view()) {
1398 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1399 to allow having midi notes on top of note lines and waveforms.
1401 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1403 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1406 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1407 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1408 ghost->add_note(note);
1412 ghost->set_height ();
1413 ghost->set_duration (_region->length() / samples_per_unit);
1414 ghosts.push_back (ghost);
1416 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1422 /** Begin tracking note state for successive calls to add_event
1425 MidiRegionView::begin_write()
1427 assert(!_active_notes);
1428 _active_notes = new CanvasNote*[128];
1429 for (unsigned i=0; i < 128; ++i) {
1430 _active_notes[i] = 0;
1435 /** Destroy note state for add_event
1438 MidiRegionView::end_write()
1440 delete[] _active_notes;
1442 _marked_for_selection.clear();
1443 _marked_for_velocity.clear();
1447 /** Resolve an active MIDI note (while recording).
1450 MidiRegionView::resolve_note(uint8_t note, double end_time)
1452 if (midi_view()->note_mode() != Sustained) {
1456 if (_active_notes && _active_notes[note]) {
1458 /* XXX is end_time really region-centric? I think so, because
1459 this is a new region that we're recording, so source zero is
1460 the same as region zero
1462 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1464 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1465 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1466 _active_notes[note] = 0;
1471 /** Extend active notes to rightmost edge of region (if length is changed)
1474 MidiRegionView::extend_active_notes()
1476 if (!_active_notes) {
1480 for (unsigned i=0; i < 128; ++i) {
1481 if (_active_notes[i]) {
1482 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1489 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1491 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1495 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1497 if (!route_ui || !route_ui->midi_track()) {
1501 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1507 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1509 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1513 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1515 if (!route_ui || !route_ui->midi_track()) {
1519 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1521 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1530 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1532 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1533 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1535 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1536 (note->note() <= midi_stream_view()->highest_note());
1541 /** Update a canvas note's size from its model note.
1542 * @param ev Canvas note to update.
1543 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1546 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1548 boost::shared_ptr<NoteType> note = ev->note();
1550 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time()));
1551 const double y1 = midi_stream_view()->note_to_y(note->note());
1553 ev->property_x1() = x;
1554 ev->property_y1() = y1;
1556 /* trim note display to not overlap the end of its region */
1558 if (note->length() > 0) {
1559 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1560 ev->property_x2() = trackview.editor().frame_to_pixel (note_end_frames);
1562 ev->property_x2() = trackview.editor().frame_to_pixel (_region->length());
1565 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1567 if (note->length() == 0) {
1568 if (_active_notes) {
1569 assert(note->note() < 128);
1570 // If this note is already active there's a stuck note,
1571 // finish the old note rectangle
1572 if (_active_notes[note->note()]) {
1573 CanvasNote* const old_rect = _active_notes[note->note()];
1574 boost::shared_ptr<NoteType> old_note = old_rect->note();
1575 old_rect->property_x2() = x;
1576 old_rect->property_outline_what() = (guint32) 0xF;
1578 _active_notes[note->note()] = ev;
1580 /* outline all but right edge */
1581 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1583 /* outline all edges */
1584 ev->property_outline_what() = (guint32) 0xF;
1587 if (update_ghost_regions) {
1588 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1589 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1591 gr->update_note (ev);
1598 MidiRegionView::update_hit (CanvasHit* ev)
1600 boost::shared_ptr<NoteType> note = ev->note();
1602 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1603 const double x = trackview.editor().frame_to_pixel(note_start_frames);
1604 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1605 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1609 return diamond_size;
1612 /** Add a MIDI note to the view (with length).
1614 * If in sustained mode, notes with length 0 will be considered active
1615 * notes, and resolve_note should be called when the corresponding note off
1616 * event arrives, to properly display the note.
1619 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1621 CanvasNoteEvent* event = 0;
1623 assert(note->time() >= 0);
1624 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1626 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1628 if (midi_view()->note_mode() == Sustained) {
1630 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1632 update_note (ev_rect);
1636 MidiGhostRegion* gr;
1638 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1639 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1640 gr->add_note(ev_rect);
1644 } else if (midi_view()->note_mode() == Percussive) {
1646 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1648 CanvasHit* ev_diamond = new CanvasHit (*this, *_note_group, diamond_size, note);
1650 update_hit (ev_diamond);
1659 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1660 note_selected(event, true);
1663 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1664 event->show_velocity();
1667 event->on_channel_selection_change(_last_channel_selection);
1668 _events.push_back(event);
1677 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1678 MidiStreamView* const view = mtv->midi_view();
1680 view->update_note_range (note->note());
1684 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1685 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1687 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1689 /* potentially extend region to hold new note */
1691 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1692 framepos_t region_end = _region->last_frame();
1694 if (end_frame > region_end) {
1695 _region->set_length (end_frame - _region->position());
1698 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1699 MidiStreamView* const view = mtv->midi_view();
1701 view->update_note_range(new_note->note());
1703 _marked_for_selection.clear ();
1706 start_note_diff_command (_("step add"));
1707 note_diff_add_note (new_note, true, false);
1710 // last_step_edit_note = new_note;
1714 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1716 change_note_lengths (false, false, beats, false, true);
1720 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
1722 assert (patch->time() >= 0);
1724 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (patch->time()));
1726 double const height = midi_stream_view()->contents_height();
1728 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1729 new CanvasPatchChange(*this, *_note_group,
1734 _custom_device_mode,
1738 // Show unless patch change is beyond the region bounds
1739 if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1740 patch_change->hide();
1742 patch_change->show();
1745 _patch_changes.push_back (patch_change);
1749 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1751 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1752 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1756 if (i != _model->patch_changes().end()) {
1757 key.msb = (*i)->bank_msb ();
1758 key.lsb = (*i)->bank_lsb ();
1759 key.program_number = (*i)->program ();
1761 key.msb = key.lsb = key.program_number = 0;
1764 assert (key.is_sane());
1769 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1771 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1773 if (pc.patch()->program() != new_patch.program_number) {
1774 c->change_program (pc.patch (), new_patch.program_number);
1777 int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1778 if (pc.patch()->bank() != new_bank) {
1779 c->change_bank (pc.patch (), new_bank);
1782 _model->apply_command (*trackview.session(), c);
1784 _patch_changes.clear ();
1785 display_patch_changes ();
1789 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1791 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1793 if (old_change->time() != new_change.time()) {
1794 c->change_time (old_change, new_change.time());
1797 if (old_change->channel() != new_change.channel()) {
1798 c->change_channel (old_change, new_change.channel());
1801 if (old_change->program() != new_change.program()) {
1802 c->change_program (old_change, new_change.program());
1805 if (old_change->bank() != new_change.bank()) {
1806 c->change_bank (old_change, new_change.bank());
1809 _model->apply_command (*trackview.session(), c);
1811 _patch_changes.clear ();
1812 display_patch_changes ();
1815 /** Add a patch change to the region.
1816 * @param t Time in frames relative to region position
1817 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1818 * MidiTimeAxisView::get_channel_for_add())
1821 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1823 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1825 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1826 c->add (MidiModel::PatchChangePtr (
1827 new Evoral::PatchChange<Evoral::MusicalTime> (
1828 absolute_frames_to_source_beats (_region->position() + t),
1829 mtv->get_channel_for_add(), patch.program(), patch.bank()
1834 _model->apply_command (*trackview.session(), c);
1836 _patch_changes.clear ();
1837 display_patch_changes ();
1841 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1843 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1844 c->change_time (pc.patch (), t);
1845 _model->apply_command (*trackview.session(), c);
1847 _patch_changes.clear ();
1848 display_patch_changes ();
1852 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1854 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1855 c->remove (pc->patch ());
1856 _model->apply_command (*trackview.session(), c);
1858 _patch_changes.clear ();
1859 display_patch_changes ();
1863 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1865 if (patch.patch()->program() < 127) {
1866 MIDI::Name::PatchPrimaryKey key;
1867 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1868 key.program_number++;
1869 change_patch_change (patch, key);
1874 MidiRegionView::next_patch (CanvasPatchChange& patch)
1876 if (patch.patch()->program() > 0) {
1877 MIDI::Name::PatchPrimaryKey key;
1878 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1879 key.program_number--;
1880 change_patch_change (patch, key);
1885 MidiRegionView::previous_bank (CanvasPatchChange& patch)
1887 if (patch.patch()->program() < 127) {
1888 MIDI::Name::PatchPrimaryKey key;
1889 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1892 change_patch_change (patch, key);
1897 change_patch_change (patch, key);
1904 MidiRegionView::next_bank (CanvasPatchChange& patch)
1906 if (patch.patch()->program() > 0) {
1907 MIDI::Name::PatchPrimaryKey key;
1908 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1909 if (key.lsb < 127) {
1911 change_patch_change (patch, key);
1913 if (key.msb < 127) {
1916 change_patch_change (patch, key);
1923 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1925 if (_selection.empty()) {
1929 _selection.erase (cne);
1933 MidiRegionView::delete_selection()
1935 if (_selection.empty()) {
1939 start_note_diff_command (_("delete selection"));
1941 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1942 if ((*i)->selected()) {
1943 _note_diff_command->remove((*i)->note());
1953 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1955 start_note_diff_command (_("delete note"));
1956 _note_diff_command->remove (n);
1959 trackview.editor().verbose_cursor()->hide ();
1963 MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev)
1965 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1967 Selection::iterator tmp = i;
1970 (*i)->set_selected (false);
1971 (*i)->hide_velocity ();
1972 _selection.erase (i);
1980 /* this does not change the status of this regionview w.r.t the editor
1986 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1988 clear_selection_except (ev);
1990 /* don't bother with checking to see if we should remove this
1991 regionview from the editor selection, since we're about to add
1992 another note, and thus put/keep this regionview in the editor
1996 if (!ev->selected()) {
1997 add_to_selection (ev);
2002 MidiRegionView::select_all_notes ()
2006 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2007 add_to_selection (*i);
2012 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2014 uint8_t low_note = 127;
2015 uint8_t high_note = 0;
2016 MidiModel::Notes& notes (_model->notes());
2017 _optimization_iterator = _events.begin();
2023 if (extend && _selection.empty()) {
2029 /* scan existing selection to get note range */
2031 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2032 if ((*i)->note()->note() < low_note) {
2033 low_note = (*i)->note()->note();
2035 if ((*i)->note()->note() > high_note) {
2036 high_note = (*i)->note()->note();
2040 low_note = min (low_note, notenum);
2041 high_note = max (high_note, notenum);
2044 _no_sound_notes = true;
2046 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2048 boost::shared_ptr<NoteType> note (*n);
2049 CanvasNoteEvent* cne;
2050 bool select = false;
2052 if (((1 << note->channel()) & channel_mask) != 0) {
2054 if ((note->note() >= low_note && note->note() <= high_note)) {
2057 } else if (note->note() == notenum) {
2063 if ((cne = find_canvas_note (note)) != 0) {
2064 // extend is false because we've taken care of it,
2065 // since it extends by time range, not pitch.
2066 note_selected (cne, add, false);
2070 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2074 _no_sound_notes = false;
2078 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2080 MidiModel::Notes& notes (_model->notes());
2081 _optimization_iterator = _events.begin();
2083 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2085 boost::shared_ptr<NoteType> note (*n);
2086 CanvasNoteEvent* cne;
2088 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2089 if ((cne = find_canvas_note (note)) != 0) {
2090 if (cne->selected()) {
2091 note_deselected (cne);
2093 note_selected (cne, true, false);
2101 MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2104 clear_selection_except (ev);
2105 if (!_selection.empty()) {
2106 PublicEditor& editor (trackview.editor());
2107 editor.get_selection().add (this);
2113 if (!ev->selected()) {
2114 add_to_selection (ev);
2118 /* find end of latest note selected, select all between that and the start of "ev" */
2120 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2121 Evoral::MusicalTime latest = 0;
2123 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2124 if ((*i)->note()->end_time() > latest) {
2125 latest = (*i)->note()->end_time();
2127 if ((*i)->note()->time() < earliest) {
2128 earliest = (*i)->note()->time();
2132 if (ev->note()->end_time() > latest) {
2133 latest = ev->note()->end_time();
2136 if (ev->note()->time() < earliest) {
2137 earliest = ev->note()->time();
2140 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2142 /* find notes entirely within OR spanning the earliest..latest range */
2144 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2145 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2146 add_to_selection (*i);
2154 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2156 remove_from_selection (ev);
2160 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
2170 // TODO: Make this faster by storing the last updated selection rect, and only
2171 // adjusting things that are in the area that appears/disappeared.
2172 // We probably need a tree to be able to find events in O(log(n)) time.
2174 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2176 /* check if any corner of the note is inside the rect
2179 1) this is computing "touched by", not "contained by" the rect.
2180 2) this does not require that events be sorted in time.
2183 const double ix1 = (*i)->x1();
2184 const double ix2 = (*i)->x2();
2185 const double iy1 = (*i)->y1();
2186 const double iy2 = (*i)->y2();
2188 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2189 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2190 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2191 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2194 if (!(*i)->selected()) {
2195 add_to_selection (*i);
2197 } else if ((*i)->selected() && !extend) {
2198 // Not inside rectangle
2199 remove_from_selection (*i);
2205 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2207 Selection::iterator i = _selection.find (ev);
2209 if (i != _selection.end()) {
2210 _selection.erase (i);
2213 ev->set_selected (false);
2214 ev->hide_velocity ();
2216 if (_selection.empty()) {
2217 PublicEditor& editor (trackview.editor());
2218 editor.get_selection().remove (this);
2223 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2225 bool add_mrv_selection = false;
2227 if (_selection.empty()) {
2228 add_mrv_selection = true;
2231 if (_selection.insert (ev).second) {
2232 ev->set_selected (true);
2233 play_midi_note ((ev)->note());
2236 if (add_mrv_selection) {
2237 PublicEditor& editor (trackview.editor());
2238 editor.get_selection().add (this);
2243 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2245 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2246 PossibleChord to_play;
2247 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2249 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2250 if ((*i)->note()->time() < earliest) {
2251 earliest = (*i)->note()->time();
2255 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2256 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2257 to_play.push_back ((*i)->note());
2259 (*i)->move_event(dx, dy);
2262 if (dy && !_selection.empty() && !_no_sound_notes && trackview.editor().sound_notes()) {
2264 if (to_play.size() > 1) {
2266 PossibleChord shifted;
2268 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2269 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2270 moved_note->set_note (moved_note->note() + cumulative_dy);
2271 shifted.push_back (moved_note);
2274 play_midi_chord (shifted);
2276 } else if (!to_play.empty()) {
2278 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2279 moved_note->set_note (moved_note->note() + cumulative_dy);
2280 play_midi_note (moved_note);
2286 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2288 assert (!_selection.empty());
2290 uint8_t lowest_note_in_selection = 127;
2291 uint8_t highest_note_in_selection = 0;
2292 uint8_t highest_note_difference = 0;
2294 // find highest and lowest notes first
2296 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2297 uint8_t pitch = (*i)->note()->note();
2298 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2299 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2303 cerr << "dnote: " << (int) dnote << endl;
2304 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2305 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2306 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2307 << int(highest_note_in_selection) << endl;
2308 cerr << "selection size: " << _selection.size() << endl;
2309 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2312 // Make sure the note pitch does not exceed the MIDI standard range
2313 if (highest_note_in_selection + dnote > 127) {
2314 highest_note_difference = highest_note_in_selection - 127;
2317 start_note_diff_command (_("move notes"));
2319 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2321 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (source_beats_to_absolute_frames ((*i)->note()->time()) + dt);
2327 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2329 uint8_t original_pitch = (*i)->note()->note();
2330 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2332 // keep notes in standard midi range
2333 clamp_to_0_127(new_pitch);
2335 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2336 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2338 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2343 // care about notes being moved beyond the upper/lower bounds on the canvas
2344 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2345 highest_note_in_selection > midi_stream_view()->highest_note()) {
2346 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2351 MidiRegionView::snap_pixel_to_frame(double x)
2353 PublicEditor& editor (trackview.editor());
2354 return snap_frame_to_frame (editor.pixel_to_frame (x));
2358 MidiRegionView::snap_to_pixel(double x)
2360 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2364 MidiRegionView::get_position_pixels()
2366 framepos_t region_frame = get_position();
2367 return trackview.editor().frame_to_pixel(region_frame);
2371 MidiRegionView::get_end_position_pixels()
2373 framepos_t frame = get_position() + get_duration ();
2374 return trackview.editor().frame_to_pixel(frame);
2378 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2380 /* the time converter will return the frame corresponding to `beats'
2381 relative to the start of the source. The start of the source
2382 is an implied position given by region->position - region->start
2384 const framepos_t source_start = _region->position() - _region->start();
2385 return source_start + _source_relative_time_converter.to (beats);
2389 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2391 /* the `frames' argument needs to be converted into a frame count
2392 relative to the start of the source before being passed in to the
2395 const framepos_t source_start = _region->position() - _region->start();
2396 return _source_relative_time_converter.from (frames - source_start);
2400 MidiRegionView::region_beats_to_region_frames(double beats) const
2402 return _region_relative_time_converter.to(beats);
2406 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2408 return _region_relative_time_converter.from(frames);
2412 MidiRegionView::begin_resizing (bool /*at_front*/)
2414 _resize_data.clear();
2416 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2417 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2419 // only insert CanvasNotes into the map
2421 NoteResizeData *resize_data = new NoteResizeData();
2422 resize_data->canvas_note = note;
2424 // create a new SimpleRect from the note which will be the resize preview
2425 SimpleRect *resize_rect = new SimpleRect(
2426 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2428 // calculate the colors: get the color settings
2429 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2430 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2433 // make the resize preview notes more transparent and bright
2434 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2436 // calculate color based on note velocity
2437 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2438 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2442 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2443 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2445 resize_data->resize_rect = resize_rect;
2446 _resize_data.push_back(resize_data);
2451 /** Update resizing notes while user drags.
2452 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2453 * @param at_front which end of the note (true == note on, false == note off)
2454 * @param delta_x change in mouse position since the start of the drag
2455 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2456 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2457 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2458 * as the \a primary note.
2461 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2463 bool cursor_set = false;
2465 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2466 SimpleRect* resize_rect = (*i)->resize_rect;
2467 CanvasNote* canvas_note = (*i)->canvas_note;
2472 current_x = canvas_note->x1() + delta_x;
2474 current_x = primary->x1() + delta_x;
2478 current_x = canvas_note->x2() + delta_x;
2480 current_x = primary->x2() + delta_x;
2485 resize_rect->property_x1() = snap_to_pixel(current_x);
2486 resize_rect->property_x2() = canvas_note->x2();
2488 resize_rect->property_x2() = snap_to_pixel(current_x);
2489 resize_rect->property_x1() = canvas_note->x1();
2495 beats = snap_pixel_to_frame (current_x);
2496 /* XXX not sure this is correct - snap_pixel_to_frame()
2497 returns an absolute frame.
2499 beats = region_frames_to_region_beats (beats);
2504 if (beats < canvas_note->note()->end_time()) {
2505 len = canvas_note->note()->time() - beats;
2506 len += canvas_note->note()->length();
2511 if (beats >= canvas_note->note()->time()) {
2512 len = beats - canvas_note->note()->time();
2519 snprintf (buf, sizeof (buf), "%.3g beats", len);
2520 show_verbose_cursor (buf, 0, 0);
2529 /** Finish resizing notes when the user releases the mouse button.
2530 * Parameters the same as for \a update_resizing().
2533 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2535 start_note_diff_command (_("resize notes"));
2537 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2538 CanvasNote* canvas_note = (*i)->canvas_note;
2539 SimpleRect* resize_rect = (*i)->resize_rect;
2541 /* Get the new x position for this resize, which is in pixels relative
2542 * to the region position.
2549 current_x = canvas_note->x1() + delta_x;
2551 current_x = primary->x1() + delta_x;
2555 current_x = canvas_note->x2() + delta_x;
2557 current_x = primary->x2() + delta_x;
2561 /* Convert that to a frame within the region */
2562 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2564 /* and then to beats */
2565 /* XXX not sure this is correct - snap_pixel_to_frame()
2566 returns an absolute frame.
2568 current_x = region_frames_to_region_beats (current_x);
2570 if (at_front && current_x < canvas_note->note()->end_time()) {
2571 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2573 double len = canvas_note->note()->time() - current_x;
2574 len += canvas_note->note()->length();
2577 /* XXX convert to beats */
2578 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2583 double len = current_x - canvas_note->note()->time();
2586 /* XXX convert to beats */
2587 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2595 _resize_data.clear();
2600 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2602 uint8_t new_velocity;
2605 new_velocity = event->note()->velocity() + velocity;
2606 clamp_to_0_127(new_velocity);
2608 new_velocity = velocity;
2611 event->set_selected (event->selected()); // change color
2613 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2617 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2622 new_note = event->note()->note() + note;
2627 clamp_to_0_127 (new_note);
2628 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2632 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2634 bool change_start = false;
2635 bool change_length = false;
2636 Evoral::MusicalTime new_start = 0;
2637 Evoral::MusicalTime new_length = 0;
2639 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2641 front_delta: if positive - move the start of the note later in time (shortening it)
2642 if negative - move the start of the note earlier in time (lengthening it)
2644 end_delta: if positive - move the end of the note later in time (lengthening it)
2645 if negative - move the end of the note earlier in time (shortening it)
2649 if (front_delta < 0) {
2651 if (event->note()->time() < -front_delta) {
2654 new_start = event->note()->time() + front_delta; // moves earlier
2657 /* start moved toward zero, so move the end point out to where it used to be.
2658 Note that front_delta is negative, so this increases the length.
2661 new_length = event->note()->length() - front_delta;
2662 change_start = true;
2663 change_length = true;
2667 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2669 if (new_pos < event->note()->end_time()) {
2670 new_start = event->note()->time() + front_delta;
2671 /* start moved toward the end, so move the end point back to where it used to be */
2672 new_length = event->note()->length() - front_delta;
2673 change_start = true;
2674 change_length = true;
2681 bool can_change = true;
2682 if (end_delta < 0) {
2683 if (event->note()->length() < -end_delta) {
2689 new_length = event->note()->length() + end_delta;
2690 change_length = true;
2695 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2698 if (change_length) {
2699 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2704 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2706 uint8_t new_channel;
2710 if (event->note()->channel() < -chn) {
2713 new_channel = event->note()->channel() + chn;
2716 new_channel = event->note()->channel() + chn;
2719 new_channel = (uint8_t) chn;
2722 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2726 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2728 Evoral::MusicalTime new_time;
2732 if (event->note()->time() < -delta) {
2735 new_time = event->note()->time() + delta;
2738 new_time = event->note()->time() + delta;
2744 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2748 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2750 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2754 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2758 if (_selection.empty()) {
2773 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2774 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2780 start_note_diff_command (_("change velocities"));
2782 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2783 Selection::iterator next = i;
2785 change_note_velocity (*i, delta, true);
2791 if (!_selection.empty()) {
2793 snprintf (buf, sizeof (buf), "Vel %d",
2794 (int) (*_selection.begin())->note()->velocity());
2795 show_verbose_cursor (buf, 10, 10);
2801 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2803 if (_selection.empty()) {
2820 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2822 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2826 if ((int8_t) (*i)->note()->note() + delta > 127) {
2833 start_note_diff_command (_("transpose"));
2835 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2836 Selection::iterator next = i;
2838 change_note_note (*i, delta, true);
2846 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2852 /* grab the current grid distance */
2854 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2856 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2857 cerr << "Grid type not available as beats - TO BE FIXED\n";
2867 start_note_diff_command (_("change note lengths"));
2869 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2870 Selection::iterator next = i;
2873 /* note the negation of the delta for start */
2875 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2884 MidiRegionView::nudge_notes (bool forward)
2886 if (_selection.empty()) {
2890 /* pick a note as the point along the timeline to get the nudge distance.
2891 its not necessarily the earliest note, so we may want to pull the notes out
2892 into a vector and sort before using the first one.
2895 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
2897 framepos_t distance;
2899 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2901 /* grid is off - use nudge distance */
2903 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2909 framepos_t next_pos = ref_point;
2912 if (max_framepos - 1 < next_pos) {
2916 if (next_pos == 0) {
2922 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2923 distance = ref_point - next_pos;
2926 if (distance == 0) {
2930 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
2936 start_note_diff_command (_("nudge"));
2938 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2939 Selection::iterator next = i;
2941 change_note_time (*i, delta, true);
2949 MidiRegionView::change_channel(uint8_t channel)
2951 start_note_diff_command(_("change channel"));
2952 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2953 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2961 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2963 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2965 _pre_enter_cursor = editor->get_canvas_cursor ();
2967 if (_mouse_state == SelectTouchDragging) {
2968 note_selected (ev, true);
2971 show_verbose_cursor (ev->note ());
2975 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2977 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2979 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2980 (*i)->hide_velocity ();
2983 editor->verbose_cursor()->hide ();
2985 if (_pre_enter_cursor) {
2986 editor->set_canvas_cursor (_pre_enter_cursor);
2987 _pre_enter_cursor = 0;
2992 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
2995 /* XXX should get patch name if we can */
2996 s << _("Bank:") << (ev->patch()->bank() + MIDI_BP_ZERO) << '\n' << _("Program:") << ((int) ev->patch()->program()) + MIDI_BP_ZERO << '\n' << _("Channel:") << ((int) ev->patch()->channel() + 1);
2997 show_verbose_cursor (s.str(), 10, 20);
3001 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
3003 trackview.editor().verbose_cursor()->hide ();
3007 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3009 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3011 if (x_fraction > 0.0 && x_fraction < 0.25) {
3012 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3013 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
3014 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3016 if (_pre_enter_cursor && can_set_cursor) {
3017 editor->set_canvas_cursor (_pre_enter_cursor);
3023 MidiRegionView::set_frame_color()
3027 TimeAxisViewItem::set_frame_color ();
3034 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3035 } else if (high_enough_for_name) {
3036 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3041 if (!rect_visible) {
3042 f = UINT_RGBA_CHANGE_A (f, 0);
3045 frame->property_fill_color_rgba() = f;
3049 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3053 case FilterChannels:
3054 _force_channel = -1;
3057 _force_channel = mask;
3058 mask = 0xFFFF; // Show all notes as active (below)
3061 // Update notes for selection
3062 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3063 (*i)->on_channel_selection_change(mask);
3066 _last_channel_selection = mask;
3068 _patch_changes.clear ();
3069 display_patch_changes ();
3073 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
3075 _model_name = model;
3076 _custom_device_mode = custom_device_mode;
3081 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3083 if (_selection.empty()) {
3087 PublicEditor& editor (trackview.editor());
3091 /* XXX what to do ? */
3095 editor.get_cut_buffer().add (selection_as_cut_buffer());
3103 start_note_diff_command();
3105 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3112 note_diff_remove_note (*i);
3122 MidiRegionView::selection_as_cut_buffer () const
3126 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3127 NoteType* n = (*i)->note().get();
3128 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3131 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3137 /** This method handles undo */
3139 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3145 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3147 trackview.session()->begin_reversible_command (_("paste"));
3149 start_note_diff_command (_("paste"));
3151 Evoral::MusicalTime beat_delta;
3152 Evoral::MusicalTime paste_pos_beats;
3153 Evoral::MusicalTime duration;
3154 Evoral::MusicalTime end_point = 0;
3156 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3157 paste_pos_beats = region_frames_to_region_beats (pos - _region->position());
3158 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3159 paste_pos_beats = 0;
3161 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",
3162 (*mcb.notes().begin())->time(),
3163 (*mcb.notes().rbegin())->end_time(),
3164 duration, pos, _region->position(),
3165 paste_pos_beats, beat_delta));
3169 for (int n = 0; n < (int) times; ++n) {
3171 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3173 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3174 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3176 /* make all newly added notes selected */
3178 note_diff_add_note (copied_note, true);
3179 end_point = copied_note->end_time();
3182 paste_pos_beats += duration;
3185 /* if we pasted past the current end of the region, extend the region */
3187 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3188 framepos_t region_end = _region->position() + _region->length() - 1;
3190 if (end_frame > region_end) {
3192 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3194 _region->clear_changes ();
3195 _region->set_length (end_frame);
3196 trackview.session()->add_command (new StatefulDiffCommand (_region));
3201 trackview.session()->commit_reversible_command ();
3204 struct EventNoteTimeEarlyFirstComparator {
3205 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3206 return a->note()->time() < b->note()->time();
3211 MidiRegionView::time_sort_events ()
3213 if (!_sort_needed) {
3217 EventNoteTimeEarlyFirstComparator cmp;
3220 _sort_needed = false;
3224 MidiRegionView::goto_next_note (bool add_to_selection)
3226 bool use_next = false;
3228 if (_events.back()->selected()) {
3232 time_sort_events ();
3234 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3235 if ((*i)->selected()) {
3238 } else if (use_next) {
3239 if (!add_to_selection) {
3242 note_selected (*i, true, false);
3248 /* use the first one */
3250 unique_select (_events.front());
3255 MidiRegionView::goto_previous_note (bool add_to_selection)
3257 bool use_next = false;
3259 if (_events.front()->selected()) {
3263 time_sort_events ();
3265 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3266 if ((*i)->selected()) {
3269 } else if (use_next) {
3270 if (!add_to_selection) {
3273 note_selected (*i, true, false);
3279 /* use the last one */
3281 unique_select (*(_events.rbegin()));
3285 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3287 bool had_selected = false;
3289 time_sort_events ();
3291 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3292 if ((*i)->selected()) {
3293 selected.insert ((*i)->note());
3294 had_selected = true;
3298 if (allow_all_if_none_selected && !had_selected) {
3299 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3300 selected.insert ((*i)->note());
3306 MidiRegionView::update_ghost_note (double x, double y)
3308 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3313 _note_group->w2i (x, y);
3315 PublicEditor& editor = trackview.editor ();
3317 framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3318 framecnt_t grid_frames;
3319 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3321 /* use region_frames... because we are converting a delta within the region
3324 double length = region_frames_to_region_beats (snap_frame_to_frame (f + grid_frames) - f);
3326 /* note that this sets the time of the ghost note in beats relative to
3327 the start of the source; that is how all note times are stored.
3329 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3330 _ghost_note->note()->set_length (length);
3331 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3332 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3334 /* the ghost note does not appear in ghost regions, so pass false in here */
3335 update_note (_ghost_note, false);
3337 show_verbose_cursor (_ghost_note->note ());
3341 MidiRegionView::create_ghost_note (double x, double y)
3346 boost::shared_ptr<NoteType> g (new NoteType);
3347 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3348 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3349 update_ghost_note (x, y);
3350 _ghost_note->show ();
3355 show_verbose_cursor (_ghost_note->note ());
3359 MidiRegionView::snap_changed ()
3365 create_ghost_note (_last_ghost_x, _last_ghost_y);
3369 MidiRegionView::drop_down_keys ()
3371 _mouse_state = None;
3375 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3377 double note = midi_stream_view()->y_to_note(y);
3379 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3381 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3383 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3384 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3385 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3386 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3391 bool add_mrv_selection = false;
3393 if (_selection.empty()) {
3394 add_mrv_selection = true;
3397 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3398 if (_selection.insert (*i).second) {
3399 (*i)->set_selected (true);
3403 if (add_mrv_selection) {
3404 PublicEditor& editor (trackview.editor());
3405 editor.get_selection().add (this);
3410 MidiRegionView::color_handler ()
3412 RegionView::color_handler ();
3414 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3415 (*i)->set_selected ((*i)->selected()); // will change color
3418 /* XXX probably more to do here */
3422 MidiRegionView::enable_display (bool yn)
3424 RegionView::enable_display (yn);
3431 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3433 if (_step_edit_cursor == 0) {
3434 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3436 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3437 _step_edit_cursor->property_y1() = 0;
3438 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3439 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3440 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3443 move_step_edit_cursor (pos);
3444 _step_edit_cursor->show ();
3448 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3450 _step_edit_cursor_position = pos;
3452 if (_step_edit_cursor) {
3453 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3454 _step_edit_cursor->property_x1() = pixel;
3455 set_step_edit_cursor_width (_step_edit_cursor_width);
3460 MidiRegionView::hide_step_edit_cursor ()
3462 if (_step_edit_cursor) {
3463 _step_edit_cursor->hide ();
3468 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3470 _step_edit_cursor_width = beats;
3472 if (_step_edit_cursor) {
3473 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3477 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3478 * @param buf Data that has been recorded.
3479 * @param w Source that this data will end up in.
3482 MidiRegionView::data_recorded (boost::shared_ptr<MidiBuffer> buf, boost::weak_ptr<MidiSource> w)
3484 if (!_active_notes) {
3485 /* we aren't actively being recorded to */
3489 boost::shared_ptr<MidiSource> src = w.lock ();
3490 if (!src || src != midi_region()->midi_source()) {
3491 /* recorded data was not destined for our source */
3495 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3496 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3498 framepos_t back = max_framepos;
3500 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3501 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3502 assert (ev.buffer ());
3504 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3506 if (ev.type() == MIDI_CMD_NOTE_ON) {
3508 boost::shared_ptr<NoteType> note (
3509 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3512 add_note (note, true);
3514 /* fix up our note range */
3515 if (ev.note() < _current_range_min) {
3516 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3517 } else if (ev.note() > _current_range_max) {
3518 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3521 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3522 resolve_note (ev.note (), time_beats);
3528 midi_stream_view()->check_record_layers (region(), back);
3532 MidiRegionView::trim_front_starting ()
3534 /* Reparent the note group to the region view's parent, so that it doesn't change
3535 when the region view is trimmed.
3537 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3538 _temporary_note_group->move (group->property_x(), group->property_y());
3539 _note_group->reparent (*_temporary_note_group);
3543 MidiRegionView::trim_front_ending ()
3545 _note_group->reparent (*group);
3546 delete _temporary_note_group;
3547 _temporary_note_group = 0;
3549 if (_region->start() < 0) {
3550 /* Trim drag made start time -ve; fix this */
3551 midi_region()->fix_negative_start ();
3556 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3558 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3559 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3563 change_patch_change (pc->patch(), d.patch ());
3568 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3571 snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3572 Evoral::midi_note_name (n->note()).c_str(),
3574 (int) n->channel() + 1,
3575 (int) n->velocity());
3577 show_verbose_cursor (buf, 10, 20);
3581 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3585 trackview.editor().get_pointer_position (wx, wy);
3590 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3592 double x1, y1, x2, y2;
3593 trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3595 if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3596 wy -= (y2 - y1) + 2 * yoffset;
3599 trackview.editor().verbose_cursor()->set (text, wx, wy);
3600 trackview.editor().verbose_cursor()->show ();
3603 /** @param p A session framepos.
3604 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3605 * @return p snapped to the grid subdivision underneath it.
3608 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3610 PublicEditor& editor = trackview.editor ();
3613 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3619 grid_frames = region_beats_to_region_frames (grid_beats);
3621 /* Hack so that we always snap to the note that we are over, instead of snapping
3622 to the next one if we're more than halfway through the one we're over.
3624 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3625 p -= grid_frames / 2;
3628 return snap_frame_to_frame (p);