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 i = _events.erase (i);
1166 _patch_changes.clear();
1170 display_patch_changes ();
1172 _marked_for_selection.clear ();
1173 _marked_for_velocity.clear ();
1175 /* we may have caused _events to contain things out of order (e.g. if a note
1176 moved earlier or later). we don't generally need them in time order, but
1177 make a note that a sort is required for those cases that require it.
1180 _sort_needed = true;
1184 MidiRegionView::display_patch_changes ()
1186 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1187 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1189 for (uint8_t i = 0; i < 16; ++i) {
1190 if (chn_mask & (1<<i)) {
1191 display_patch_changes_on_channel (i);
1193 /* TODO gray-out patch instad of not displaying it */
1198 MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
1200 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1202 if ((*i)->channel() != channel) {
1206 MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
1208 boost::shared_ptr<MIDI::Name::Patch> patch =
1209 MIDI::Name::MidiPatchManager::instance().find_patch(
1210 _model_name, _custom_device_mode, channel, patch_key);
1213 add_canvas_patch_change (*i, patch->name());
1216 /* program and bank numbers are zero-based: convert to one-based: MIDI_BP_ZERO */
1217 snprintf (buf, 16, "%d %d", (*i)->program() + MIDI_BP_ZERO , (*i)->bank() + MIDI_BP_ZERO);
1218 add_canvas_patch_change (*i, buf);
1224 MidiRegionView::display_sysexes()
1226 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1227 Evoral::MusicalTime time = (*i)->time();
1232 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1233 str << int((*i)->buffer()[b]);
1234 if (b != (*i)->size() -1) {
1238 string text = str.str();
1240 const double x = trackview.editor().frame_to_pixel(source_beats_to_absolute_frames(time));
1242 double height = midi_stream_view()->contents_height();
1244 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1245 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1247 // Show unless patch change is beyond the region bounds
1248 if (time - _region->start() >= _region->length() || time < _region->start()) {
1254 _sys_exes.push_back(sysex);
1259 MidiRegionView::~MidiRegionView ()
1261 in_destructor = true;
1263 trackview.editor().verbose_cursor()->hide ();
1265 note_delete_connection.disconnect ();
1267 delete _list_editor;
1269 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1271 if (_active_notes) {
1279 delete _note_diff_command;
1280 delete _step_edit_cursor;
1281 delete _temporary_note_group;
1285 MidiRegionView::region_resized (const PropertyChange& what_changed)
1287 RegionView::region_resized(what_changed);
1289 if (what_changed.contains (ARDOUR::Properties::position)) {
1290 set_duration(_region->length(), 0);
1291 if (_enable_display) {
1298 MidiRegionView::reset_width_dependent_items (double pixel_width)
1300 RegionView::reset_width_dependent_items(pixel_width);
1301 assert(_pixel_width == pixel_width);
1303 if (_enable_display) {
1307 move_step_edit_cursor (_step_edit_cursor_position);
1308 set_step_edit_cursor_width (_step_edit_cursor_width);
1312 MidiRegionView::set_height (double height)
1314 static const double FUDGE = 2.0;
1315 const double old_height = _height;
1316 RegionView::set_height(height);
1317 _height = height - FUDGE;
1319 apply_note_range(midi_stream_view()->lowest_note(),
1320 midi_stream_view()->highest_note(),
1321 height != old_height + FUDGE);
1324 name_pixbuf->raise_to_top();
1327 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1328 (*x)->set_height (midi_stream_view()->contents_height());
1331 if (_step_edit_cursor) {
1332 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1337 /** Apply the current note range from the stream view
1338 * by repositioning/hiding notes as necessary
1341 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1343 if (!_enable_display) {
1347 if (!force && _current_range_min == min && _current_range_max == max) {
1351 _current_range_min = min;
1352 _current_range_max = max;
1354 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1355 CanvasNoteEvent* event = *i;
1356 boost::shared_ptr<NoteType> note (event->note());
1358 if (note->note() < _current_range_min ||
1359 note->note() > _current_range_max) {
1365 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1367 const double y1 = midi_stream_view()->note_to_y(note->note());
1368 const double y2 = y1 + floor(midi_stream_view()->note_height());
1370 cnote->property_y1() = y1;
1371 cnote->property_y2() = y2;
1373 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1375 const double diamond_size = update_hit (chit);
1377 chit->set_height (diamond_size);
1383 MidiRegionView::add_ghost (TimeAxisView& tv)
1387 double unit_position = _region->position () / samples_per_unit;
1388 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1389 MidiGhostRegion* ghost;
1391 if (mtv && mtv->midi_view()) {
1392 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1393 to allow having midi notes on top of note lines and waveforms.
1395 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1397 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1400 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1401 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1402 ghost->add_note(note);
1406 ghost->set_height ();
1407 ghost->set_duration (_region->length() / samples_per_unit);
1408 ghosts.push_back (ghost);
1410 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1416 /** Begin tracking note state for successive calls to add_event
1419 MidiRegionView::begin_write()
1421 assert(!_active_notes);
1422 _active_notes = new CanvasNote*[128];
1423 for (unsigned i=0; i < 128; ++i) {
1424 _active_notes[i] = 0;
1429 /** Destroy note state for add_event
1432 MidiRegionView::end_write()
1434 delete[] _active_notes;
1436 _marked_for_selection.clear();
1437 _marked_for_velocity.clear();
1441 /** Resolve an active MIDI note (while recording).
1444 MidiRegionView::resolve_note(uint8_t note, double end_time)
1446 if (midi_view()->note_mode() != Sustained) {
1450 if (_active_notes && _active_notes[note]) {
1452 /* XXX is end_time really region-centric? I think so, because
1453 this is a new region that we're recording, so source zero is
1454 the same as region zero
1456 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1458 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1459 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1460 _active_notes[note] = 0;
1465 /** Extend active notes to rightmost edge of region (if length is changed)
1468 MidiRegionView::extend_active_notes()
1470 if (!_active_notes) {
1474 for (unsigned i=0; i < 128; ++i) {
1475 if (_active_notes[i]) {
1476 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1483 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1485 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1489 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1491 if (!route_ui || !route_ui->midi_track()) {
1495 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1501 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1503 if (_no_sound_notes || !trackview.editor().sound_notes()) {
1507 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1509 if (!route_ui || !route_ui->midi_track()) {
1513 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1515 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1524 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1526 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1527 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1529 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1530 (note->note() <= midi_stream_view()->highest_note());
1535 /** Update a canvas note's size from its model note.
1536 * @param ev Canvas note to update.
1537 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1540 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1542 boost::shared_ptr<NoteType> note = ev->note();
1544 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time()));
1545 const double y1 = midi_stream_view()->note_to_y(note->note());
1547 ev->property_x1() = x;
1548 ev->property_y1() = y1;
1550 /* trim note display to not overlap the end of its region */
1552 if (note->length() > 0) {
1553 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1554 ev->property_x2() = trackview.editor().frame_to_pixel (note_end_frames);
1556 ev->property_x2() = trackview.editor().frame_to_pixel (_region->length());
1559 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1561 if (note->length() == 0) {
1562 if (_active_notes) {
1563 assert(note->note() < 128);
1564 // If this note is already active there's a stuck note,
1565 // finish the old note rectangle
1566 if (_active_notes[note->note()]) {
1567 CanvasNote* const old_rect = _active_notes[note->note()];
1568 boost::shared_ptr<NoteType> old_note = old_rect->note();
1569 old_rect->property_x2() = x;
1570 old_rect->property_outline_what() = (guint32) 0xF;
1572 _active_notes[note->note()] = ev;
1574 /* outline all but right edge */
1575 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1577 /* outline all edges */
1578 ev->property_outline_what() = (guint32) 0xF;
1581 if (update_ghost_regions) {
1582 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1583 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1585 gr->update_note (ev);
1592 MidiRegionView::update_hit (CanvasHit* ev)
1594 boost::shared_ptr<NoteType> note = ev->note();
1596 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1597 const double x = trackview.editor().frame_to_pixel(note_start_frames);
1598 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1599 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1603 return diamond_size;
1606 /** Add a MIDI note to the view (with length).
1608 * If in sustained mode, notes with length 0 will be considered active
1609 * notes, and resolve_note should be called when the corresponding note off
1610 * event arrives, to properly display the note.
1613 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1615 CanvasNoteEvent* event = 0;
1617 assert(note->time() >= 0);
1618 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1620 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1622 if (midi_view()->note_mode() == Sustained) {
1624 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1626 update_note (ev_rect);
1630 MidiGhostRegion* gr;
1632 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1633 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1634 gr->add_note(ev_rect);
1638 } else if (midi_view()->note_mode() == Percussive) {
1640 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1642 CanvasHit* ev_diamond = new CanvasHit (*this, *_note_group, diamond_size, note);
1644 update_hit (ev_diamond);
1653 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1654 note_selected(event, true);
1657 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1658 event->show_velocity();
1661 event->on_channel_selection_change(_last_channel_selection);
1662 _events.push_back(event);
1671 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1672 MidiStreamView* const view = mtv->midi_view();
1674 view->update_note_range (note->note());
1678 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1679 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1681 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1683 /* potentially extend region to hold new note */
1685 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1686 framepos_t region_end = _region->last_frame();
1688 if (end_frame > region_end) {
1689 _region->set_length (end_frame - _region->position());
1692 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1693 MidiStreamView* const view = mtv->midi_view();
1695 view->update_note_range(new_note->note());
1697 _marked_for_selection.clear ();
1700 start_note_diff_command (_("step add"));
1701 note_diff_add_note (new_note, true, false);
1704 // last_step_edit_note = new_note;
1708 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1710 change_note_lengths (false, false, beats, false, true);
1714 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
1716 assert (patch->time() >= 0);
1718 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (patch->time()));
1720 double const height = midi_stream_view()->contents_height();
1722 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1723 new CanvasPatchChange(*this, *_note_group,
1728 _custom_device_mode,
1732 // Show unless patch change is beyond the region bounds
1733 if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1734 patch_change->hide();
1736 patch_change->show();
1739 _patch_changes.push_back (patch_change);
1743 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1745 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1746 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1750 if (i != _model->patch_changes().end()) {
1751 key.msb = (*i)->bank_msb ();
1752 key.lsb = (*i)->bank_lsb ();
1753 key.program_number = (*i)->program ();
1755 key.msb = key.lsb = key.program_number = 0;
1758 assert (key.is_sane());
1763 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1765 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1767 if (pc.patch()->program() != new_patch.program_number) {
1768 c->change_program (pc.patch (), new_patch.program_number);
1771 int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1772 if (pc.patch()->bank() != new_bank) {
1773 c->change_bank (pc.patch (), new_bank);
1776 _model->apply_command (*trackview.session(), c);
1778 _patch_changes.clear ();
1779 display_patch_changes ();
1783 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1785 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1787 if (old_change->time() != new_change.time()) {
1788 c->change_time (old_change, new_change.time());
1791 if (old_change->channel() != new_change.channel()) {
1792 c->change_channel (old_change, new_change.channel());
1795 if (old_change->program() != new_change.program()) {
1796 c->change_program (old_change, new_change.program());
1799 if (old_change->bank() != new_change.bank()) {
1800 c->change_bank (old_change, new_change.bank());
1803 _model->apply_command (*trackview.session(), c);
1805 _patch_changes.clear ();
1806 display_patch_changes ();
1809 /** Add a patch change to the region.
1810 * @param t Time in frames relative to region position
1811 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1812 * MidiTimeAxisView::get_channel_for_add())
1815 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1817 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1819 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1820 c->add (MidiModel::PatchChangePtr (
1821 new Evoral::PatchChange<Evoral::MusicalTime> (
1822 absolute_frames_to_source_beats (_region->position() + t),
1823 mtv->get_channel_for_add(), patch.program(), patch.bank()
1828 _model->apply_command (*trackview.session(), c);
1830 _patch_changes.clear ();
1831 display_patch_changes ();
1835 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1837 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1838 c->change_time (pc.patch (), t);
1839 _model->apply_command (*trackview.session(), c);
1841 _patch_changes.clear ();
1842 display_patch_changes ();
1846 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1848 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1849 c->remove (pc->patch ());
1850 _model->apply_command (*trackview.session(), c);
1852 _patch_changes.clear ();
1853 display_patch_changes ();
1857 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1859 if (patch.patch()->program() < 127) {
1860 MIDI::Name::PatchPrimaryKey key;
1861 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1862 key.program_number++;
1863 change_patch_change (patch, key);
1868 MidiRegionView::next_patch (CanvasPatchChange& patch)
1870 if (patch.patch()->program() > 0) {
1871 MIDI::Name::PatchPrimaryKey key;
1872 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1873 key.program_number--;
1874 change_patch_change (patch, key);
1879 MidiRegionView::previous_bank (CanvasPatchChange& patch)
1881 if (patch.patch()->program() < 127) {
1882 MIDI::Name::PatchPrimaryKey key;
1883 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1886 change_patch_change (patch, key);
1891 change_patch_change (patch, key);
1898 MidiRegionView::next_bank (CanvasPatchChange& patch)
1900 if (patch.patch()->program() > 0) {
1901 MIDI::Name::PatchPrimaryKey key;
1902 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1903 if (key.lsb < 127) {
1905 change_patch_change (patch, key);
1907 if (key.msb < 127) {
1910 change_patch_change (patch, key);
1917 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1919 if (_selection.empty()) {
1923 _selection.erase (cne);
1927 MidiRegionView::delete_selection()
1929 if (_selection.empty()) {
1933 start_note_diff_command (_("delete selection"));
1935 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1936 if ((*i)->selected()) {
1937 _note_diff_command->remove((*i)->note());
1947 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1949 start_note_diff_command (_("delete note"));
1950 _note_diff_command->remove (n);
1953 trackview.editor().verbose_cursor()->hide ();
1957 MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal)
1959 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1961 Selection::iterator tmp = i;
1964 (*i)->set_selected (false);
1965 (*i)->hide_velocity ();
1966 _selection.erase (i);
1974 /* this does not change the status of this regionview w.r.t the editor
1979 SelectionCleared (this); /* EMIT SIGNAL */
1984 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1986 clear_selection_except (ev);
1988 /* don't bother with checking to see if we should remove this
1989 regionview from the editor selection, since we're about to add
1990 another note, and thus put/keep this regionview in the editor
1994 if (!ev->selected()) {
1995 add_to_selection (ev);
2000 MidiRegionView::select_all_notes ()
2004 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2005 add_to_selection (*i);
2010 MidiRegionView::select_range (framepos_t start, framepos_t end)
2014 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2015 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2016 if (t >= start && t <= end) {
2017 add_to_selection (*i);
2023 MidiRegionView::invert_selection ()
2025 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2026 if ((*i)->selected()) {
2027 remove_from_selection(*i);
2029 add_to_selection (*i);
2035 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2037 uint8_t low_note = 127;
2038 uint8_t high_note = 0;
2039 MidiModel::Notes& notes (_model->notes());
2040 _optimization_iterator = _events.begin();
2046 if (extend && _selection.empty()) {
2052 /* scan existing selection to get note range */
2054 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2055 if ((*i)->note()->note() < low_note) {
2056 low_note = (*i)->note()->note();
2058 if ((*i)->note()->note() > high_note) {
2059 high_note = (*i)->note()->note();
2063 low_note = min (low_note, notenum);
2064 high_note = max (high_note, notenum);
2067 _no_sound_notes = true;
2069 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2071 boost::shared_ptr<NoteType> note (*n);
2072 CanvasNoteEvent* cne;
2073 bool select = false;
2075 if (((1 << note->channel()) & channel_mask) != 0) {
2077 if ((note->note() >= low_note && note->note() <= high_note)) {
2080 } else if (note->note() == notenum) {
2086 if ((cne = find_canvas_note (note)) != 0) {
2087 // extend is false because we've taken care of it,
2088 // since it extends by time range, not pitch.
2089 note_selected (cne, add, false);
2093 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2097 _no_sound_notes = false;
2101 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2103 MidiModel::Notes& notes (_model->notes());
2104 _optimization_iterator = _events.begin();
2106 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2108 boost::shared_ptr<NoteType> note (*n);
2109 CanvasNoteEvent* cne;
2111 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2112 if ((cne = find_canvas_note (note)) != 0) {
2113 if (cne->selected()) {
2114 note_deselected (cne);
2116 note_selected (cne, true, false);
2124 MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2127 clear_selection_except (ev);
2128 if (!_selection.empty()) {
2129 PublicEditor& editor (trackview.editor());
2130 editor.get_selection().add (this);
2136 if (!ev->selected()) {
2137 add_to_selection (ev);
2141 /* find end of latest note selected, select all between that and the start of "ev" */
2143 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2144 Evoral::MusicalTime latest = 0;
2146 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2147 if ((*i)->note()->end_time() > latest) {
2148 latest = (*i)->note()->end_time();
2150 if ((*i)->note()->time() < earliest) {
2151 earliest = (*i)->note()->time();
2155 if (ev->note()->end_time() > latest) {
2156 latest = ev->note()->end_time();
2159 if (ev->note()->time() < earliest) {
2160 earliest = ev->note()->time();
2163 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2165 /* find notes entirely within OR spanning the earliest..latest range */
2167 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2168 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2169 add_to_selection (*i);
2177 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2179 remove_from_selection (ev);
2183 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
2193 // TODO: Make this faster by storing the last updated selection rect, and only
2194 // adjusting things that are in the area that appears/disappeared.
2195 // We probably need a tree to be able to find events in O(log(n)) time.
2197 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2199 /* check if any corner of the note is inside the rect
2202 1) this is computing "touched by", not "contained by" the rect.
2203 2) this does not require that events be sorted in time.
2206 const double ix1 = (*i)->x1();
2207 const double ix2 = (*i)->x2();
2208 const double iy1 = (*i)->y1();
2209 const double iy2 = (*i)->y2();
2211 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2212 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2213 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2214 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2217 if (!(*i)->selected()) {
2218 add_to_selection (*i);
2220 } else if ((*i)->selected() && !extend) {
2221 // Not inside rectangle
2222 remove_from_selection (*i);
2228 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2230 Selection::iterator i = _selection.find (ev);
2232 if (i != _selection.end()) {
2233 _selection.erase (i);
2236 ev->set_selected (false);
2237 ev->hide_velocity ();
2239 if (_selection.empty()) {
2240 PublicEditor& editor (trackview.editor());
2241 editor.get_selection().remove (this);
2246 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2248 bool add_mrv_selection = false;
2250 if (_selection.empty()) {
2251 add_mrv_selection = true;
2254 if (_selection.insert (ev).second) {
2255 ev->set_selected (true);
2256 play_midi_note ((ev)->note());
2259 if (add_mrv_selection) {
2260 PublicEditor& editor (trackview.editor());
2261 editor.get_selection().add (this);
2266 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2268 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2269 PossibleChord to_play;
2270 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2272 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2273 if ((*i)->note()->time() < earliest) {
2274 earliest = (*i)->note()->time();
2278 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2279 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2280 to_play.push_back ((*i)->note());
2282 (*i)->move_event(dx, dy);
2285 if (dy && !_selection.empty() && !_no_sound_notes && trackview.editor().sound_notes()) {
2287 if (to_play.size() > 1) {
2289 PossibleChord shifted;
2291 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2292 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2293 moved_note->set_note (moved_note->note() + cumulative_dy);
2294 shifted.push_back (moved_note);
2297 play_midi_chord (shifted);
2299 } else if (!to_play.empty()) {
2301 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2302 moved_note->set_note (moved_note->note() + cumulative_dy);
2303 play_midi_note (moved_note);
2309 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2311 assert (!_selection.empty());
2313 uint8_t lowest_note_in_selection = 127;
2314 uint8_t highest_note_in_selection = 0;
2315 uint8_t highest_note_difference = 0;
2317 // find highest and lowest notes first
2319 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2320 uint8_t pitch = (*i)->note()->note();
2321 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2322 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2326 cerr << "dnote: " << (int) dnote << endl;
2327 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2328 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2329 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2330 << int(highest_note_in_selection) << endl;
2331 cerr << "selection size: " << _selection.size() << endl;
2332 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2335 // Make sure the note pitch does not exceed the MIDI standard range
2336 if (highest_note_in_selection + dnote > 127) {
2337 highest_note_difference = highest_note_in_selection - 127;
2340 start_note_diff_command (_("move notes"));
2342 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2344 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (source_beats_to_absolute_frames ((*i)->note()->time()) + dt);
2350 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2352 uint8_t original_pitch = (*i)->note()->note();
2353 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2355 // keep notes in standard midi range
2356 clamp_to_0_127(new_pitch);
2358 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2359 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2361 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2366 // care about notes being moved beyond the upper/lower bounds on the canvas
2367 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2368 highest_note_in_selection > midi_stream_view()->highest_note()) {
2369 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2373 /** @param x Pixel relative to the region position.
2374 * @return Snapped frame relative to the region position.
2377 MidiRegionView::snap_pixel_to_frame(double x)
2379 PublicEditor& editor (trackview.editor());
2380 return snap_frame_to_frame (editor.pixel_to_frame (x));
2383 /** @param x Pixel relative to the region position.
2384 * @return Snapped pixel relative to the region position.
2387 MidiRegionView::snap_to_pixel(double x)
2389 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2393 MidiRegionView::get_position_pixels()
2395 framepos_t region_frame = get_position();
2396 return trackview.editor().frame_to_pixel(region_frame);
2400 MidiRegionView::get_end_position_pixels()
2402 framepos_t frame = get_position() + get_duration ();
2403 return trackview.editor().frame_to_pixel(frame);
2407 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2409 /* the time converter will return the frame corresponding to `beats'
2410 relative to the start of the source. The start of the source
2411 is an implied position given by region->position - region->start
2413 const framepos_t source_start = _region->position() - _region->start();
2414 return source_start + _source_relative_time_converter.to (beats);
2418 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2420 /* the `frames' argument needs to be converted into a frame count
2421 relative to the start of the source before being passed in to the
2424 const framepos_t source_start = _region->position() - _region->start();
2425 return _source_relative_time_converter.from (frames - source_start);
2429 MidiRegionView::region_beats_to_region_frames(double beats) const
2431 return _region_relative_time_converter.to(beats);
2435 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2437 return _region_relative_time_converter.from(frames);
2441 MidiRegionView::begin_resizing (bool /*at_front*/)
2443 _resize_data.clear();
2445 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2446 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2448 // only insert CanvasNotes into the map
2450 NoteResizeData *resize_data = new NoteResizeData();
2451 resize_data->canvas_note = note;
2453 // create a new SimpleRect from the note which will be the resize preview
2454 SimpleRect *resize_rect = new SimpleRect(
2455 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2457 // calculate the colors: get the color settings
2458 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2459 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2462 // make the resize preview notes more transparent and bright
2463 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2465 // calculate color based on note velocity
2466 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2467 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2471 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2472 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2474 resize_data->resize_rect = resize_rect;
2475 _resize_data.push_back(resize_data);
2480 /** Update resizing notes while user drags.
2481 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2482 * @param at_front which end of the note (true == note on, false == note off)
2483 * @param delta_x change in mouse position since the start of the drag
2484 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2485 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2486 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2487 * as the \a primary note.
2490 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2492 bool cursor_set = false;
2494 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2495 SimpleRect* resize_rect = (*i)->resize_rect;
2496 CanvasNote* canvas_note = (*i)->canvas_note;
2501 current_x = canvas_note->x1() + delta_x;
2503 current_x = primary->x1() + delta_x;
2507 current_x = canvas_note->x2() + delta_x;
2509 current_x = primary->x2() + delta_x;
2514 resize_rect->property_x1() = snap_to_pixel(current_x);
2515 resize_rect->property_x2() = canvas_note->x2();
2517 resize_rect->property_x2() = snap_to_pixel(current_x);
2518 resize_rect->property_x1() = canvas_note->x1();
2524 beats = snap_pixel_to_frame (current_x);
2525 beats = region_frames_to_region_beats (beats);
2530 if (beats < canvas_note->note()->end_time()) {
2531 len = canvas_note->note()->time() - beats;
2532 len += canvas_note->note()->length();
2537 if (beats >= canvas_note->note()->time()) {
2538 len = beats - canvas_note->note()->time();
2545 snprintf (buf, sizeof (buf), "%.3g beats", len);
2546 show_verbose_cursor (buf, 0, 0);
2555 /** Finish resizing notes when the user releases the mouse button.
2556 * Parameters the same as for \a update_resizing().
2559 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2561 start_note_diff_command (_("resize notes"));
2563 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2564 CanvasNote* canvas_note = (*i)->canvas_note;
2565 SimpleRect* resize_rect = (*i)->resize_rect;
2567 /* Get the new x position for this resize, which is in pixels relative
2568 * to the region position.
2575 current_x = canvas_note->x1() + delta_x;
2577 current_x = primary->x1() + delta_x;
2581 current_x = canvas_note->x2() + delta_x;
2583 current_x = primary->x2() + delta_x;
2587 /* Convert that to a frame within the source */
2588 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2590 /* and then to beats */
2591 current_x = region_frames_to_region_beats (current_x);
2593 if (at_front && current_x < canvas_note->note()->end_time()) {
2594 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2596 double len = canvas_note->note()->time() - current_x;
2597 len += canvas_note->note()->length();
2600 /* XXX convert to beats */
2601 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2606 double len = current_x - canvas_note->note()->time();
2609 /* XXX convert to beats */
2610 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2618 _resize_data.clear();
2623 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2625 uint8_t new_velocity;
2628 new_velocity = event->note()->velocity() + velocity;
2629 clamp_to_0_127(new_velocity);
2631 new_velocity = velocity;
2634 event->set_selected (event->selected()); // change color
2636 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2640 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2645 new_note = event->note()->note() + note;
2650 clamp_to_0_127 (new_note);
2651 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2655 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2657 bool change_start = false;
2658 bool change_length = false;
2659 Evoral::MusicalTime new_start = 0;
2660 Evoral::MusicalTime new_length = 0;
2662 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2664 front_delta: if positive - move the start of the note later in time (shortening it)
2665 if negative - move the start of the note earlier in time (lengthening it)
2667 end_delta: if positive - move the end of the note later in time (lengthening it)
2668 if negative - move the end of the note earlier in time (shortening it)
2672 if (front_delta < 0) {
2674 if (event->note()->time() < -front_delta) {
2677 new_start = event->note()->time() + front_delta; // moves earlier
2680 /* start moved toward zero, so move the end point out to where it used to be.
2681 Note that front_delta is negative, so this increases the length.
2684 new_length = event->note()->length() - front_delta;
2685 change_start = true;
2686 change_length = true;
2690 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2692 if (new_pos < event->note()->end_time()) {
2693 new_start = event->note()->time() + front_delta;
2694 /* start moved toward the end, so move the end point back to where it used to be */
2695 new_length = event->note()->length() - front_delta;
2696 change_start = true;
2697 change_length = true;
2704 bool can_change = true;
2705 if (end_delta < 0) {
2706 if (event->note()->length() < -end_delta) {
2712 new_length = event->note()->length() + end_delta;
2713 change_length = true;
2718 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2721 if (change_length) {
2722 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2727 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2729 uint8_t new_channel;
2733 if (event->note()->channel() < -chn) {
2736 new_channel = event->note()->channel() + chn;
2739 new_channel = event->note()->channel() + chn;
2742 new_channel = (uint8_t) chn;
2745 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2749 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2751 Evoral::MusicalTime new_time;
2755 if (event->note()->time() < -delta) {
2758 new_time = event->note()->time() + delta;
2761 new_time = event->note()->time() + delta;
2767 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2771 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2773 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2777 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2781 if (_selection.empty()) {
2796 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2797 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2803 start_note_diff_command (_("change velocities"));
2805 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2806 Selection::iterator next = i;
2808 change_note_velocity (*i, delta, true);
2814 if (!_selection.empty()) {
2816 snprintf (buf, sizeof (buf), "Vel %d",
2817 (int) (*_selection.begin())->note()->velocity());
2818 show_verbose_cursor (buf, 10, 10);
2824 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2826 if (_selection.empty()) {
2843 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2845 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2849 if ((int8_t) (*i)->note()->note() + delta > 127) {
2856 start_note_diff_command (_("transpose"));
2858 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2859 Selection::iterator next = i;
2861 change_note_note (*i, delta, true);
2869 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2875 /* grab the current grid distance */
2877 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2879 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2880 cerr << "Grid type not available as beats - TO BE FIXED\n";
2890 start_note_diff_command (_("change note lengths"));
2892 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2893 Selection::iterator next = i;
2896 /* note the negation of the delta for start */
2898 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2907 MidiRegionView::nudge_notes (bool forward)
2909 if (_selection.empty()) {
2913 /* pick a note as the point along the timeline to get the nudge distance.
2914 its not necessarily the earliest note, so we may want to pull the notes out
2915 into a vector and sort before using the first one.
2918 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
2920 framepos_t distance;
2922 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2924 /* grid is off - use nudge distance */
2926 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2932 framepos_t next_pos = ref_point;
2935 if (max_framepos - 1 < next_pos) {
2939 if (next_pos == 0) {
2945 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2946 distance = ref_point - next_pos;
2949 if (distance == 0) {
2953 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
2959 start_note_diff_command (_("nudge"));
2961 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2962 Selection::iterator next = i;
2964 change_note_time (*i, delta, true);
2972 MidiRegionView::change_channel(uint8_t channel)
2974 start_note_diff_command(_("change channel"));
2975 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2976 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2984 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2986 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2988 _pre_enter_cursor = editor->get_canvas_cursor ();
2990 if (_mouse_state == SelectTouchDragging) {
2991 note_selected (ev, true);
2994 show_verbose_cursor (ev->note ());
2998 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
3000 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3002 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3003 (*i)->hide_velocity ();
3006 editor->verbose_cursor()->hide ();
3008 if (_pre_enter_cursor) {
3009 editor->set_canvas_cursor (_pre_enter_cursor);
3010 _pre_enter_cursor = 0;
3015 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
3018 /* XXX should get patch name if we can */
3019 s << _("Bank:") << (ev->patch()->bank() + MIDI_BP_ZERO) << '\n' << _("Program:") << ((int) ev->patch()->program()) + MIDI_BP_ZERO << '\n' << _("Channel:") << ((int) ev->patch()->channel() + 1);
3020 show_verbose_cursor (s.str(), 10, 20);
3024 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
3026 trackview.editor().verbose_cursor()->hide ();
3030 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3032 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3034 if (x_fraction > 0.0 && x_fraction < 0.25) {
3035 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3036 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
3037 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3039 if (_pre_enter_cursor && can_set_cursor) {
3040 editor->set_canvas_cursor (_pre_enter_cursor);
3046 MidiRegionView::set_frame_color()
3050 TimeAxisViewItem::set_frame_color ();
3057 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3058 } else if (high_enough_for_name) {
3059 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3064 if (!rect_visible) {
3065 f = UINT_RGBA_CHANGE_A (f, 0);
3068 frame->property_fill_color_rgba() = f;
3072 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3076 case FilterChannels:
3077 _force_channel = -1;
3080 _force_channel = mask;
3081 mask = 0xFFFF; // Show all notes as active (below)
3084 // Update notes for selection
3085 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3086 (*i)->on_channel_selection_change(mask);
3089 _last_channel_selection = mask;
3091 _patch_changes.clear ();
3092 display_patch_changes ();
3096 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
3098 _model_name = model;
3099 _custom_device_mode = custom_device_mode;
3104 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3106 if (_selection.empty()) {
3110 PublicEditor& editor (trackview.editor());
3114 /* XXX what to do ? */
3118 editor.get_cut_buffer().add (selection_as_cut_buffer());
3126 start_note_diff_command();
3128 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3135 note_diff_remove_note (*i);
3145 MidiRegionView::selection_as_cut_buffer () const
3149 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3150 NoteType* n = (*i)->note().get();
3151 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3154 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3160 /** This method handles undo */
3162 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3168 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3170 trackview.session()->begin_reversible_command (_("paste"));
3172 start_note_diff_command (_("paste"));
3174 Evoral::MusicalTime beat_delta;
3175 Evoral::MusicalTime paste_pos_beats;
3176 Evoral::MusicalTime duration;
3177 Evoral::MusicalTime end_point = 0;
3179 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3180 paste_pos_beats = region_frames_to_region_beats (pos - _region->position());
3181 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3182 paste_pos_beats = 0;
3184 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",
3185 (*mcb.notes().begin())->time(),
3186 (*mcb.notes().rbegin())->end_time(),
3187 duration, pos, _region->position(),
3188 paste_pos_beats, beat_delta));
3192 for (int n = 0; n < (int) times; ++n) {
3194 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3196 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3197 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3199 /* make all newly added notes selected */
3201 note_diff_add_note (copied_note, true);
3202 end_point = copied_note->end_time();
3205 paste_pos_beats += duration;
3208 /* if we pasted past the current end of the region, extend the region */
3210 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3211 framepos_t region_end = _region->position() + _region->length() - 1;
3213 if (end_frame > region_end) {
3215 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3217 _region->clear_changes ();
3218 _region->set_length (end_frame);
3219 trackview.session()->add_command (new StatefulDiffCommand (_region));
3224 trackview.session()->commit_reversible_command ();
3227 struct EventNoteTimeEarlyFirstComparator {
3228 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3229 return a->note()->time() < b->note()->time();
3234 MidiRegionView::time_sort_events ()
3236 if (!_sort_needed) {
3240 EventNoteTimeEarlyFirstComparator cmp;
3243 _sort_needed = false;
3247 MidiRegionView::goto_next_note (bool add_to_selection)
3249 bool use_next = false;
3251 if (_events.back()->selected()) {
3255 time_sort_events ();
3257 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3258 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3260 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3261 if ((*i)->selected()) {
3264 } else if (use_next) {
3265 if (channel_mask & (1 << (*i)->note()->channel())) {
3266 if (!add_to_selection) {
3269 note_selected (*i, true, false);
3276 /* use the first one */
3278 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3279 unique_select (_events.front());
3284 MidiRegionView::goto_previous_note (bool add_to_selection)
3286 bool use_next = false;
3288 if (_events.front()->selected()) {
3292 time_sort_events ();
3294 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3295 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3297 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3298 if ((*i)->selected()) {
3301 } else if (use_next) {
3302 if (channel_mask & (1 << (*i)->note()->channel())) {
3303 if (!add_to_selection) {
3306 note_selected (*i, true, false);
3313 /* use the last one */
3315 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3316 unique_select (*(_events.rbegin()));
3321 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3323 bool had_selected = false;
3325 time_sort_events ();
3327 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3328 if ((*i)->selected()) {
3329 selected.insert ((*i)->note());
3330 had_selected = true;
3334 if (allow_all_if_none_selected && !had_selected) {
3335 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3336 selected.insert ((*i)->note());
3342 MidiRegionView::update_ghost_note (double x, double y)
3344 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3349 _note_group->w2i (x, y);
3351 PublicEditor& editor = trackview.editor ();
3353 framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3354 framecnt_t grid_frames;
3355 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3357 /* use region_frames... because we are converting a delta within the region
3360 double length = region_frames_to_region_beats (snap_frame_to_frame (f + grid_frames) - f);
3362 /* note that this sets the time of the ghost note in beats relative to
3363 the start of the source; that is how all note times are stored.
3365 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3366 _ghost_note->note()->set_length (length);
3367 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3368 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3370 /* the ghost note does not appear in ghost regions, so pass false in here */
3371 update_note (_ghost_note, false);
3373 show_verbose_cursor (_ghost_note->note ());
3377 MidiRegionView::create_ghost_note (double x, double y)
3382 boost::shared_ptr<NoteType> g (new NoteType);
3383 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3384 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3385 update_ghost_note (x, y);
3386 _ghost_note->show ();
3391 show_verbose_cursor (_ghost_note->note ());
3395 MidiRegionView::snap_changed ()
3401 create_ghost_note (_last_ghost_x, _last_ghost_y);
3405 MidiRegionView::drop_down_keys ()
3407 _mouse_state = None;
3411 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3413 double note = midi_stream_view()->y_to_note(y);
3415 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3417 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3419 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3420 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3421 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3422 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3427 bool add_mrv_selection = false;
3429 if (_selection.empty()) {
3430 add_mrv_selection = true;
3433 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3434 if (_selection.insert (*i).second) {
3435 (*i)->set_selected (true);
3439 if (add_mrv_selection) {
3440 PublicEditor& editor (trackview.editor());
3441 editor.get_selection().add (this);
3446 MidiRegionView::color_handler ()
3448 RegionView::color_handler ();
3450 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3451 (*i)->set_selected ((*i)->selected()); // will change color
3454 /* XXX probably more to do here */
3458 MidiRegionView::enable_display (bool yn)
3460 RegionView::enable_display (yn);
3467 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3469 if (_step_edit_cursor == 0) {
3470 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3472 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3473 _step_edit_cursor->property_y1() = 0;
3474 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3475 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3476 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3479 move_step_edit_cursor (pos);
3480 _step_edit_cursor->show ();
3484 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3486 _step_edit_cursor_position = pos;
3488 if (_step_edit_cursor) {
3489 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3490 _step_edit_cursor->property_x1() = pixel;
3491 set_step_edit_cursor_width (_step_edit_cursor_width);
3496 MidiRegionView::hide_step_edit_cursor ()
3498 if (_step_edit_cursor) {
3499 _step_edit_cursor->hide ();
3504 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3506 _step_edit_cursor_width = beats;
3508 if (_step_edit_cursor) {
3509 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3513 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3514 * @param w Source that the data will end up in.
3517 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3519 if (!_active_notes) {
3520 /* we aren't actively being recorded to */
3524 boost::shared_ptr<MidiSource> src = w.lock ();
3525 if (!src || src != midi_region()->midi_source()) {
3526 /* recorded data was not destined for our source */
3530 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3532 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3534 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3536 framepos_t back = max_framepos;
3538 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3539 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3540 assert (ev.buffer ());
3542 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3544 if (ev.type() == MIDI_CMD_NOTE_ON) {
3546 boost::shared_ptr<NoteType> note (
3547 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3550 add_note (note, true);
3552 /* fix up our note range */
3553 if (ev.note() < _current_range_min) {
3554 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3555 } else if (ev.note() > _current_range_max) {
3556 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3559 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3560 resolve_note (ev.note (), time_beats);
3566 midi_stream_view()->check_record_layers (region(), back);
3570 MidiRegionView::trim_front_starting ()
3572 /* Reparent the note group to the region view's parent, so that it doesn't change
3573 when the region view is trimmed.
3575 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3576 _temporary_note_group->move (group->property_x(), group->property_y());
3577 _note_group->reparent (*_temporary_note_group);
3581 MidiRegionView::trim_front_ending ()
3583 _note_group->reparent (*group);
3584 delete _temporary_note_group;
3585 _temporary_note_group = 0;
3587 if (_region->start() < 0) {
3588 /* Trim drag made start time -ve; fix this */
3589 midi_region()->fix_negative_start ();
3594 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3596 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3597 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3601 change_patch_change (pc->patch(), d.patch ());
3606 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3609 snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3610 Evoral::midi_note_name (n->note()).c_str(),
3612 (int) n->channel() + 1,
3613 (int) n->velocity());
3615 show_verbose_cursor (buf, 10, 20);
3619 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3623 trackview.editor().get_pointer_position (wx, wy);
3628 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3630 double x1, y1, x2, y2;
3631 trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3633 if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3634 wy -= (y2 - y1) + 2 * yoffset;
3637 trackview.editor().verbose_cursor()->set (text, wx, wy);
3638 trackview.editor().verbose_cursor()->show ();
3641 /** @param p A session framepos.
3642 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3643 * @return p snapped to the grid subdivision underneath it.
3646 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3648 PublicEditor& editor = trackview.editor ();
3651 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3657 grid_frames = region_beats_to_region_frames (grid_beats);
3659 /* Hack so that we always snap to the note that we are over, instead of snapping
3660 to the next one if we're more than halfway through the one we're over.
3662 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3663 p -= grid_frames / 2;
3666 return snap_frame_to_frame (p);
3669 /** Called when the selection has been cleared in any MidiRegionView.
3670 * @param rv MidiRegionView that the selection was cleared in.
3673 MidiRegionView::selection_cleared (MidiRegionView* rv)
3679 /* Clear our selection in sympathy; but don't signal the fact */
3680 clear_selection (false);