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/MIDIEvent.hpp"
45 #include "evoral/Control.hpp"
46 #include "evoral/midi_util.h"
48 #include "automation_region_view.h"
49 #include "automation_time_axis.h"
50 #include "canvas-hit.h"
51 #include "canvas-note.h"
52 #include "canvas_patch_change.h"
55 #include "editor_drag.h"
56 #include "ghostregion.h"
57 #include "gui_thread.h"
59 #include "midi_channel_dialog.h"
60 #include "midi_cut_buffer.h"
61 #include "midi_list_editor.h"
62 #include "midi_region_view.h"
63 #include "midi_streamview.h"
64 #include "midi_time_axis.h"
65 #include "midi_util.h"
66 #include "note_player.h"
67 #include "public_editor.h"
68 #include "rgb_macros.h"
69 #include "selection.h"
70 #include "simpleline.h"
71 #include "streamview.h"
73 #include "mouse_cursors.h"
74 #include "patch_change_dialog.h"
75 #include "verbose_cursor.h"
79 using namespace ARDOUR;
81 using namespace Editing;
82 using namespace ArdourCanvas;
83 using Gtkmm2ext::Keyboard;
85 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
87 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
89 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
90 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
91 : RegionView (parent, tv, r, spu, basic_color)
93 , _last_channel_selection(0xFFFF)
94 , _current_range_min(0)
95 , _current_range_max(0)
97 , _note_group(new ArdourCanvas::Group(*group))
98 , _note_diff_command (0)
100 , _step_edit_cursor (0)
101 , _step_edit_cursor_width (1.0)
102 , _step_edit_cursor_position (0.0)
103 , _channel_selection_scoped_note (0)
104 , _temporary_note_group (0)
107 , _sort_needed (true)
108 , _optimization_iterator (_events.end())
110 , _no_sound_notes (false)
113 , _pre_enter_cursor (0)
115 _note_group->raise_to_top();
116 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
118 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
119 connect_to_diskstream ();
121 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
124 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
125 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
126 TimeAxisViewItem::Visibility visibility)
127 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
129 , _last_channel_selection(0xFFFF)
130 , _current_range_min(0)
131 , _current_range_max(0)
133 , _note_group(new ArdourCanvas::Group(*parent))
134 , _note_diff_command (0)
136 , _step_edit_cursor (0)
137 , _step_edit_cursor_width (1.0)
138 , _step_edit_cursor_position (0.0)
139 , _channel_selection_scoped_note (0)
140 , _temporary_note_group (0)
143 , _sort_needed (true)
144 , _optimization_iterator (_events.end())
146 , _no_sound_notes (false)
149 , _pre_enter_cursor (0)
151 _note_group->raise_to_top();
152 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
154 connect_to_diskstream ();
156 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
160 MidiRegionView::parameter_changed (std::string const & p)
162 if (p == "diplay-first-midi-bank-as-zero") {
163 if (_enable_display) {
169 MidiRegionView::MidiRegionView (const MidiRegionView& other)
170 : sigc::trackable(other)
173 , _last_channel_selection(0xFFFF)
174 , _current_range_min(0)
175 , _current_range_max(0)
177 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
178 , _note_diff_command (0)
180 , _step_edit_cursor (0)
181 , _step_edit_cursor_width (1.0)
182 , _step_edit_cursor_position (0.0)
183 , _channel_selection_scoped_note (0)
184 , _temporary_note_group (0)
187 , _sort_needed (true)
188 , _optimization_iterator (_events.end())
190 , _no_sound_notes (false)
193 , _pre_enter_cursor (0)
198 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
199 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
204 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
205 : RegionView (other, boost::shared_ptr<Region> (region))
207 , _last_channel_selection(0xFFFF)
208 , _current_range_min(0)
209 , _current_range_max(0)
211 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
212 , _note_diff_command (0)
214 , _step_edit_cursor (0)
215 , _step_edit_cursor_width (1.0)
216 , _step_edit_cursor_position (0.0)
217 , _channel_selection_scoped_note (0)
218 , _temporary_note_group (0)
221 , _sort_needed (true)
222 , _optimization_iterator (_events.end())
224 , _no_sound_notes (false)
227 , _pre_enter_cursor (0)
232 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
233 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
239 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
241 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
243 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
244 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
248 midi_region()->midi_source(0)->load_model();
251 _model = midi_region()->midi_source(0)->model();
252 _enable_display = false;
254 RegionView::init (basic_color, false);
256 compute_colors (basic_color);
258 set_height (trackview.current_height());
261 region_sync_changed ();
262 region_resized (ARDOUR::bounds_change);
265 reset_width_dependent_items (_pixel_width);
269 _enable_display = true;
272 display_model (_model);
276 group->raise_to_top();
277 group->signal_event().connect(
278 sigc::mem_fun(this, &MidiRegionView::canvas_event), false);
280 midi_view()->signal_channel_mode_changed().connect(
281 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
283 midi_view()->signal_midi_patch_settings_changed().connect(
284 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
286 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
287 ui_bind(&MidiRegionView::snap_changed, this),
290 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
291 connect_to_diskstream ();
293 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
296 const boost::shared_ptr<ARDOUR::MidiRegion>
297 MidiRegionView::midi_region() const
299 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
303 MidiRegionView::connect_to_diskstream ()
305 midi_view()->midi_track()->DataRecorded.connect(
306 *this, invalidator(*this),
307 ui_bind(&MidiRegionView::data_recorded, this, _1),
312 MidiRegionView::canvas_event(GdkEvent* ev)
315 case GDK_ENTER_NOTIFY:
316 case GDK_LEAVE_NOTIFY:
317 _last_event_x = ev->crossing.x;
318 _last_event_y = ev->crossing.y;
320 case GDK_MOTION_NOTIFY:
321 _last_event_x = ev->motion.x;
322 _last_event_y = ev->motion.y;
328 if (!trackview.editor().internal_editing()) {
334 return scroll (&ev->scroll);
337 return key_press (&ev->key);
339 case GDK_KEY_RELEASE:
340 return key_release (&ev->key);
342 case GDK_BUTTON_PRESS:
343 return button_press (&ev->button);
345 case GDK_2BUTTON_PRESS:
348 case GDK_BUTTON_RELEASE:
349 return button_release (&ev->button);
351 case GDK_ENTER_NOTIFY:
352 return enter_notify (&ev->crossing);
354 case GDK_LEAVE_NOTIFY:
355 return leave_notify (&ev->crossing);
357 case GDK_MOTION_NOTIFY:
358 return motion (&ev->motion);
368 MidiRegionView::remove_ghost_note ()
375 MidiRegionView::enter_notify (GdkEventCrossing* ev)
377 trackview.editor().MouseModeChanged.connect (
378 _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
381 if (trackview.editor().current_mouse_mode() == MouseRange && _mouse_state != AddDragging) {
382 create_ghost_note (ev->x, ev->y);
385 if (!trackview.editor().internal_editing()) {
386 Keyboard::magic_widget_drop_focus();
388 Keyboard::magic_widget_grab_focus();
396 MidiRegionView::leave_notify (GdkEventCrossing*)
398 _mouse_mode_connection.disconnect ();
400 trackview.editor().verbose_cursor()->hide ();
401 remove_ghost_note ();
407 MidiRegionView::mouse_mode_changed ()
409 if (trackview.editor().current_mouse_mode() == MouseRange && trackview.editor().internal_editing()) {
410 create_ghost_note (_last_event_x, _last_event_y);
412 remove_ghost_note ();
413 trackview.editor().verbose_cursor()->hide ();
416 if (!trackview.editor().internal_editing()) {
417 Keyboard::magic_widget_drop_focus();
419 Keyboard::magic_widget_grab_focus();
425 MidiRegionView::button_press (GdkEventButton* ev)
427 if (ev->button != 1) {
431 if (_mouse_state != SelectTouchDragging) {
433 _pressed_button = ev->button;
434 _mouse_state = Pressed;
439 _pressed_button = ev->button;
445 MidiRegionView::button_release (GdkEventButton* ev)
447 double event_x, event_y;
449 if (ev->button != 1) {
456 group->w2i(event_x, event_y);
457 group->ungrab(ev->time);
459 PublicEditor& editor = trackview.editor ();
461 switch (_mouse_state) {
462 case Pressed: // Clicked
464 switch (editor.current_mouse_mode()) {
470 if (Keyboard::is_insert_note_event(ev)) {
472 double event_x, event_y;
476 group->w2i(event_x, event_y);
479 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
485 create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true, true);
493 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
499 create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true, true);
510 case SelectRectDragging:
512 editor.drags()->end_grab ((GdkEvent *) ev);
514 create_ghost_note (ev->x, ev->y);
526 MidiRegionView::motion (GdkEventMotion* ev)
528 PublicEditor& editor = trackview.editor ();
530 if (!_ghost_note && editor.current_mouse_mode() != MouseRange
531 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())
532 && _mouse_state != AddDragging) {
534 create_ghost_note (ev->x, ev->y);
535 } else if (_ghost_note && editor.current_mouse_mode() != MouseRange
536 && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
538 update_ghost_note (ev->x, ev->y);
539 } else if (_ghost_note && editor.current_mouse_mode() != MouseRange) {
541 remove_ghost_note ();
543 editor.verbose_cursor()->hide ();
544 } else if (_ghost_note && editor.current_mouse_mode() == MouseRange) {
545 update_ghost_note (ev->x, ev->y);
548 /* any motion immediately hides velocity text that may have been visible */
550 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
551 (*i)->hide_velocity ();
554 switch (_mouse_state) {
557 if (_pressed_button == 1 && editor.current_mouse_mode() == MouseObject
558 && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
560 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
561 _mouse_state = SelectRectDragging;
564 } else if (editor.internal_editing()) {
566 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
567 _mouse_state = AddDragging;
569 remove_ghost_note ();
571 editor.verbose_cursor()->hide ();
578 case SelectRectDragging:
580 editor.drags()->motion_handler ((GdkEvent *) ev, false);
583 case SelectTouchDragging:
595 MidiRegionView::scroll (GdkEventScroll* ev)
597 if (_selection.empty()) {
601 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
602 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
603 it still works for zoom.
608 trackview.editor().verbose_cursor()->hide ();
610 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
612 if (ev->direction == GDK_SCROLL_UP) {
613 change_velocities (true, fine, false);
614 } else if (ev->direction == GDK_SCROLL_DOWN) {
615 change_velocities (false, fine, false);
621 MidiRegionView::key_press (GdkEventKey* ev)
623 /* since GTK bindings are generally activated on press, and since
624 detectable auto-repeat is the name of the game and only sends
625 repeated presses, carry out key actions at key press, not release.
628 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
630 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
631 _mouse_state = SelectTouchDragging;
634 } else if (ev->keyval == GDK_Escape && unmodified) {
638 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
640 bool start = (ev->keyval == GDK_comma);
641 bool end = (ev->keyval == GDK_period);
642 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
643 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
645 change_note_lengths (fine, shorter, 0.0, start, end);
649 } else if (ev->keyval == GDK_Delete && unmodified) {
654 } else if (ev->keyval == GDK_Tab) {
656 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
657 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
659 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
663 } else if (ev->keyval == GDK_ISO_Left_Tab) {
665 /* Shift-TAB generates ISO Left Tab, for some reason */
667 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
668 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
670 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
676 } else if (ev->keyval == GDK_Up) {
678 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
679 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
681 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
682 change_velocities (true, fine, allow_smush);
684 transpose (true, fine, allow_smush);
688 } else if (ev->keyval == GDK_Down) {
690 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
691 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
693 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
694 change_velocities (false, fine, allow_smush);
696 transpose (false, fine, allow_smush);
700 } else if (ev->keyval == GDK_Left && unmodified) {
705 } else if (ev->keyval == GDK_Right && unmodified) {
710 } else if (ev->keyval == GDK_c && unmodified) {
719 MidiRegionView::key_release (GdkEventKey* ev)
721 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
729 MidiRegionView::channel_edit ()
732 uint8_t current_channel = 0;
734 if (_selection.empty()) {
738 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
740 current_channel = (*i)->note()->channel ();
745 MidiChannelDialog channel_dialog (current_channel);
746 int ret = channel_dialog.run ();
749 case Gtk::RESPONSE_OK:
755 uint8_t new_channel = channel_dialog.active_channel ();
757 start_note_diff_command (_("channel edit"));
759 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
760 Selection::iterator next = i;
762 change_note_channel (*i, new_channel);
770 MidiRegionView::show_list_editor ()
773 _list_editor = new MidiListEditor (trackview.session(), midi_region());
775 _list_editor->present ();
778 /** Add a note to the model, and the view, at a canvas (click) coordinate.
779 * \param t time in frames relative to the position of the region
780 * \param y vertical position in pixels
781 * \param length duration of the note in beats, which will be snapped to the grid
782 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
783 * \param snap_x true to snap x to the grid, otherwise false.
786 MidiRegionView::create_note_at (framepos_t t, double y, double length, bool sh, bool snap_x)
788 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
789 MidiStreamView* const view = mtv->midi_view();
791 double note = view->y_to_note(y);
794 assert(note <= 127.0);
796 // Start of note in frames relative to region start
798 framecnt_t grid_frames;
799 t = snap_frame_to_grid_underneath (t, grid_frames);
804 length = region_frames_to_region_beats(
805 snap_frame_to_frame (t + region_beats_to_region_frames(length)) - t
808 assert (length != 0);
811 length = region_frames_to_region_beats (region_beats_to_region_frames (length) - 1);
814 const boost::shared_ptr<NoteType> new_note (new NoteType (mtv->get_channel_for_add (),
815 region_frames_to_region_beats(t + _region->start()), length,
816 (uint8_t)note, 0x40));
818 if (_model->contains (new_note)) {
822 view->update_note_range(new_note->note());
824 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note");
826 _model->apply_command(*trackview.session(), cmd);
828 play_midi_note (new_note);
832 MidiRegionView::clear_events()
837 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
838 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
843 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
848 _patch_changes.clear();
850 _optimization_iterator = _events.end();
854 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
858 content_connection.disconnect ();
859 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
863 if (_enable_display) {
869 MidiRegionView::start_note_diff_command (string name)
871 if (!_note_diff_command) {
872 _note_diff_command = _model->new_note_diff_command (name);
877 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
879 if (_note_diff_command) {
880 _note_diff_command->add (note);
883 _marked_for_selection.insert(note);
886 _marked_for_velocity.insert(note);
891 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
893 if (_note_diff_command && ev->note()) {
894 _note_diff_command->remove(ev->note());
899 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
900 MidiModel::NoteDiffCommand::Property property,
903 if (_note_diff_command) {
904 _note_diff_command->change (ev->note(), property, val);
909 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
910 MidiModel::NoteDiffCommand::Property property,
911 Evoral::MusicalTime val)
913 if (_note_diff_command) {
914 _note_diff_command->change (ev->note(), property, val);
919 MidiRegionView::apply_diff (bool as_subcommand)
923 if (!_note_diff_command) {
927 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
928 // Mark all selected notes for selection when model reloads
929 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
930 _marked_for_selection.insert((*i)->note());
935 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
937 _model->apply_command (*trackview.session(), _note_diff_command);
940 _note_diff_command = 0;
941 midi_view()->midi_track()->playlist_modified();
944 _marked_for_selection.clear();
947 _marked_for_velocity.clear();
951 MidiRegionView::abort_command()
953 delete _note_diff_command;
954 _note_diff_command = 0;
959 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
961 if (_optimization_iterator != _events.end()) {
962 ++_optimization_iterator;
965 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
966 return *_optimization_iterator;
969 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
970 if ((*_optimization_iterator)->note() == note) {
971 return *_optimization_iterator;
979 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
981 MidiModel::Notes notes;
982 _model->get_notes (notes, op, val, chan_mask);
984 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
985 CanvasNoteEvent* cne = find_canvas_note (*n);
993 MidiRegionView::redisplay_model()
995 // Don't redisplay the model if we're currently recording and displaying that
1004 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1005 (*i)->invalidate ();
1008 MidiModel::ReadLock lock(_model->read_lock());
1010 MidiModel::Notes& notes (_model->notes());
1011 _optimization_iterator = _events.begin();
1013 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1015 boost::shared_ptr<NoteType> note (*n);
1016 CanvasNoteEvent* cne;
1019 if (note_in_region_range (note, visible)) {
1021 if ((cne = find_canvas_note (note)) != 0) {
1028 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1030 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1042 add_note (note, visible);
1047 if ((cne = find_canvas_note (note)) != 0) {
1055 /* remove note items that are no longer valid */
1057 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1058 if (!(*i)->valid ()) {
1060 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1061 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1063 gr->remove_note (*i);
1068 i = _events.erase (i);
1075 _patch_changes.clear();
1079 display_patch_changes ();
1081 _marked_for_selection.clear ();
1082 _marked_for_velocity.clear ();
1084 /* we may have caused _events to contain things out of order (e.g. if a note
1085 moved earlier or later). we don't generally need them in time order, but
1086 make a note that a sort is required for those cases that require it.
1089 _sort_needed = true;
1093 MidiRegionView::display_patch_changes ()
1095 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1096 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1098 for (uint8_t i = 0; i < 16; ++i) {
1099 if (chn_mask & (1<<i)) {
1100 display_patch_changes_on_channel (i);
1102 /* TODO gray-out patch instad of not displaying it */
1107 MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
1109 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1111 if ((*i)->channel() != channel) {
1115 MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
1117 boost::shared_ptr<MIDI::Name::Patch> patch =
1118 MIDI::Name::MidiPatchManager::instance().find_patch(
1119 _model_name, _custom_device_mode, channel, patch_key);
1122 add_canvas_patch_change (*i, patch->name());
1125 /* program and bank numbers are zero-based: convert to one-based: MIDI_BP_ZERO */
1126 snprintf (buf, 16, "%d %d", (*i)->program() + MIDI_BP_ZERO , (*i)->bank() + MIDI_BP_ZERO);
1127 add_canvas_patch_change (*i, buf);
1133 MidiRegionView::display_sysexes()
1135 bool have_periodic_system_messages = false;
1136 bool display_periodic_messages = true;
1138 if (!Config->get_never_display_periodic_midi()) {
1140 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1141 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1142 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1145 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1146 have_periodic_system_messages = true;
1152 if (have_periodic_system_messages) {
1153 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1155 /* get an approximate value for the number of samples per video frame */
1157 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1159 /* if we are zoomed out beyond than the cutoff (i.e. more
1160 * frames per pixel than frames per 4 video frames), don't
1161 * show periodic sysex messages.
1164 if (zoom > (video_frame*4)) {
1165 display_periodic_messages = false;
1169 display_periodic_messages = false;
1172 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1174 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1175 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1177 Evoral::MusicalTime time = (*i)->time();
1181 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1182 if (!display_periodic_messages) {
1190 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1191 str << int((*i)->buffer()[b]);
1192 if (b != (*i)->size() -1) {
1196 string text = str.str();
1198 const double x = trackview.editor().frame_to_pixel(source_beats_to_absolute_frames(time));
1200 double height = midi_stream_view()->contents_height();
1202 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1203 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1205 // Show unless message is beyond the region bounds
1206 if (time - _region->start() >= _region->length() || time < _region->start()) {
1212 _sys_exes.push_back(sysex);
1216 MidiRegionView::~MidiRegionView ()
1218 in_destructor = true;
1220 trackview.editor().verbose_cursor()->hide ();
1222 note_delete_connection.disconnect ();
1224 delete _list_editor;
1226 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1228 if (_active_notes) {
1236 delete _note_diff_command;
1237 delete _step_edit_cursor;
1238 delete _temporary_note_group;
1242 MidiRegionView::region_resized (const PropertyChange& what_changed)
1244 RegionView::region_resized(what_changed);
1246 if (what_changed.contains (ARDOUR::Properties::position)) {
1247 set_duration(_region->length(), 0);
1248 if (_enable_display) {
1255 MidiRegionView::reset_width_dependent_items (double pixel_width)
1257 RegionView::reset_width_dependent_items(pixel_width);
1258 assert(_pixel_width == pixel_width);
1260 if (_enable_display) {
1264 move_step_edit_cursor (_step_edit_cursor_position);
1265 set_step_edit_cursor_width (_step_edit_cursor_width);
1269 MidiRegionView::set_height (double height)
1271 static const double FUDGE = 2.0;
1272 const double old_height = _height;
1273 RegionView::set_height(height);
1274 _height = height - FUDGE;
1276 apply_note_range(midi_stream_view()->lowest_note(),
1277 midi_stream_view()->highest_note(),
1278 height != old_height + FUDGE);
1281 name_pixbuf->raise_to_top();
1284 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1285 (*x)->set_height (midi_stream_view()->contents_height());
1288 if (_step_edit_cursor) {
1289 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1294 /** Apply the current note range from the stream view
1295 * by repositioning/hiding notes as necessary
1298 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1300 if (!_enable_display) {
1304 if (!force && _current_range_min == min && _current_range_max == max) {
1308 _current_range_min = min;
1309 _current_range_max = max;
1311 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1312 CanvasNoteEvent* event = *i;
1313 boost::shared_ptr<NoteType> note (event->note());
1315 if (note->note() < _current_range_min ||
1316 note->note() > _current_range_max) {
1322 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1324 const double y1 = midi_stream_view()->note_to_y(note->note());
1325 const double y2 = y1 + floor(midi_stream_view()->note_height());
1327 cnote->property_y1() = y1;
1328 cnote->property_y2() = y2;
1330 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1332 const double diamond_size = update_hit (chit);
1334 chit->set_height (diamond_size);
1340 MidiRegionView::add_ghost (TimeAxisView& tv)
1344 double unit_position = _region->position () / samples_per_unit;
1345 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1346 MidiGhostRegion* ghost;
1348 if (mtv && mtv->midi_view()) {
1349 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1350 to allow having midi notes on top of note lines and waveforms.
1352 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1354 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1357 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1358 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1359 ghost->add_note(note);
1363 ghost->set_height ();
1364 ghost->set_duration (_region->length() / samples_per_unit);
1365 ghosts.push_back (ghost);
1367 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1373 /** Begin tracking note state for successive calls to add_event
1376 MidiRegionView::begin_write()
1378 assert(!_active_notes);
1379 _active_notes = new CanvasNote*[128];
1380 for (unsigned i=0; i < 128; ++i) {
1381 _active_notes[i] = 0;
1386 /** Destroy note state for add_event
1389 MidiRegionView::end_write()
1391 delete[] _active_notes;
1393 _marked_for_selection.clear();
1394 _marked_for_velocity.clear();
1398 /** Resolve an active MIDI note (while recording).
1401 MidiRegionView::resolve_note(uint8_t note, double end_time)
1403 if (midi_view()->note_mode() != Sustained) {
1407 if (_active_notes && _active_notes[note]) {
1409 /* XXX is end_time really region-centric? I think so, because
1410 this is a new region that we're recording, so source zero is
1411 the same as region zero
1413 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1415 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1416 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1417 _active_notes[note] = 0;
1422 /** Extend active notes to rightmost edge of region (if length is changed)
1425 MidiRegionView::extend_active_notes()
1427 if (!_active_notes) {
1431 for (unsigned i=0; i < 128; ++i) {
1432 if (_active_notes[i]) {
1433 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1440 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1442 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1446 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1448 if (!route_ui || !route_ui->midi_track()) {
1452 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1458 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1460 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1464 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1466 if (!route_ui || !route_ui->midi_track()) {
1470 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1472 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1481 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1483 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1484 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1486 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1487 (note->note() <= midi_stream_view()->highest_note());
1492 /** Update a canvas note's size from its model note.
1493 * @param ev Canvas note to update.
1494 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1497 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1499 boost::shared_ptr<NoteType> note = ev->note();
1501 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time()));
1502 const double y1 = midi_stream_view()->note_to_y(note->note());
1504 ev->property_x1() = x;
1505 ev->property_y1() = y1;
1507 /* trim note display to not overlap the end of its region */
1509 if (note->length() > 0) {
1510 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1511 ev->property_x2() = trackview.editor().frame_to_pixel (note_end_frames);
1513 ev->property_x2() = trackview.editor().frame_to_pixel (_region->length());
1516 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1518 if (note->length() == 0) {
1519 if (_active_notes) {
1520 assert(note->note() < 128);
1521 // If this note is already active there's a stuck note,
1522 // finish the old note rectangle
1523 if (_active_notes[note->note()]) {
1524 CanvasNote* const old_rect = _active_notes[note->note()];
1525 boost::shared_ptr<NoteType> old_note = old_rect->note();
1526 old_rect->property_x2() = x;
1527 old_rect->property_outline_what() = (guint32) 0xF;
1529 _active_notes[note->note()] = ev;
1531 /* outline all but right edge */
1532 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1534 /* outline all edges */
1535 ev->property_outline_what() = (guint32) 0xF;
1538 if (update_ghost_regions) {
1539 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1540 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1542 gr->update_note (ev);
1549 MidiRegionView::update_hit (CanvasHit* ev)
1551 boost::shared_ptr<NoteType> note = ev->note();
1553 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1554 const double x = trackview.editor().frame_to_pixel(note_start_frames);
1555 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1556 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1560 return diamond_size;
1563 /** Add a MIDI note to the view (with length).
1565 * If in sustained mode, notes with length 0 will be considered active
1566 * notes, and resolve_note should be called when the corresponding note off
1567 * event arrives, to properly display the note.
1570 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1572 CanvasNoteEvent* event = 0;
1574 assert(note->time() >= 0);
1575 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1577 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1579 if (midi_view()->note_mode() == Sustained) {
1581 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1583 update_note (ev_rect);
1587 MidiGhostRegion* gr;
1589 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1590 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1591 gr->add_note(ev_rect);
1595 } else if (midi_view()->note_mode() == Percussive) {
1597 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1599 CanvasHit* ev_diamond = new CanvasHit (*this, *_note_group, diamond_size, note);
1601 update_hit (ev_diamond);
1610 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1611 note_selected(event, true);
1614 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1615 event->show_velocity();
1618 event->on_channel_selection_change(_last_channel_selection);
1619 _events.push_back(event);
1628 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1629 MidiStreamView* const view = mtv->midi_view();
1631 view->update_note_range (note->note());
1635 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1636 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1638 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1640 /* potentially extend region to hold new note */
1642 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1643 framepos_t region_end = _region->last_frame();
1645 if (end_frame > region_end) {
1646 _region->set_length (end_frame - _region->position());
1649 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1650 MidiStreamView* const view = mtv->midi_view();
1652 view->update_note_range(new_note->note());
1654 _marked_for_selection.clear ();
1657 start_note_diff_command (_("step add"));
1658 note_diff_add_note (new_note, true, false);
1661 // last_step_edit_note = new_note;
1665 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1667 change_note_lengths (false, false, beats, false, true);
1671 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
1673 assert (patch->time() >= 0);
1675 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (patch->time()));
1677 double const height = midi_stream_view()->contents_height();
1679 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1680 new CanvasPatchChange(*this, *_note_group,
1685 _custom_device_mode,
1689 // Show unless patch change is beyond the region bounds
1690 if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1691 patch_change->hide();
1693 patch_change->show();
1696 _patch_changes.push_back (patch_change);
1700 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1702 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1703 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1707 if (i != _model->patch_changes().end()) {
1708 key.msb = (*i)->bank_msb ();
1709 key.lsb = (*i)->bank_lsb ();
1710 key.program_number = (*i)->program ();
1712 key.msb = key.lsb = key.program_number = 0;
1715 assert (key.is_sane());
1720 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1722 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1724 if (pc.patch()->program() != new_patch.program_number) {
1725 c->change_program (pc.patch (), new_patch.program_number);
1728 int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1729 if (pc.patch()->bank() != new_bank) {
1730 c->change_bank (pc.patch (), new_bank);
1733 _model->apply_command (*trackview.session(), c);
1735 _patch_changes.clear ();
1736 display_patch_changes ();
1740 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1742 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1744 if (old_change->time() != new_change.time()) {
1745 c->change_time (old_change, new_change.time());
1748 if (old_change->channel() != new_change.channel()) {
1749 c->change_channel (old_change, new_change.channel());
1752 if (old_change->program() != new_change.program()) {
1753 c->change_program (old_change, new_change.program());
1756 if (old_change->bank() != new_change.bank()) {
1757 c->change_bank (old_change, new_change.bank());
1760 _model->apply_command (*trackview.session(), c);
1762 _patch_changes.clear ();
1763 display_patch_changes ();
1766 /** Add a patch change to the region.
1767 * @param t Time in frames relative to region position
1768 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1769 * MidiTimeAxisView::get_channel_for_add())
1772 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1774 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1776 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1777 c->add (MidiModel::PatchChangePtr (
1778 new Evoral::PatchChange<Evoral::MusicalTime> (
1779 absolute_frames_to_source_beats (_region->position() + t),
1780 mtv->get_channel_for_add(), patch.program(), patch.bank()
1785 _model->apply_command (*trackview.session(), c);
1787 _patch_changes.clear ();
1788 display_patch_changes ();
1792 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1794 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1795 c->change_time (pc.patch (), t);
1796 _model->apply_command (*trackview.session(), c);
1798 _patch_changes.clear ();
1799 display_patch_changes ();
1803 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1805 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1806 c->remove (pc->patch ());
1807 _model->apply_command (*trackview.session(), c);
1809 _patch_changes.clear ();
1810 display_patch_changes ();
1814 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1816 if (patch.patch()->program() < 127) {
1817 MIDI::Name::PatchPrimaryKey key;
1818 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1819 key.program_number++;
1820 change_patch_change (patch, key);
1825 MidiRegionView::next_patch (CanvasPatchChange& patch)
1827 if (patch.patch()->program() > 0) {
1828 MIDI::Name::PatchPrimaryKey key;
1829 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1830 key.program_number--;
1831 change_patch_change (patch, key);
1836 MidiRegionView::previous_bank (CanvasPatchChange& patch)
1838 if (patch.patch()->program() < 127) {
1839 MIDI::Name::PatchPrimaryKey key;
1840 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1843 change_patch_change (patch, key);
1848 change_patch_change (patch, key);
1855 MidiRegionView::next_bank (CanvasPatchChange& patch)
1857 if (patch.patch()->program() > 0) {
1858 MIDI::Name::PatchPrimaryKey key;
1859 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1860 if (key.lsb < 127) {
1862 change_patch_change (patch, key);
1864 if (key.msb < 127) {
1867 change_patch_change (patch, key);
1874 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1876 if (_selection.empty()) {
1880 _selection.erase (cne);
1884 MidiRegionView::delete_selection()
1886 if (_selection.empty()) {
1890 start_note_diff_command (_("delete selection"));
1892 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1893 if ((*i)->selected()) {
1894 _note_diff_command->remove((*i)->note());
1904 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1906 start_note_diff_command (_("delete note"));
1907 _note_diff_command->remove (n);
1910 trackview.editor().verbose_cursor()->hide ();
1914 MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal)
1916 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1918 Selection::iterator tmp = i;
1921 (*i)->set_selected (false);
1922 (*i)->hide_velocity ();
1923 _selection.erase (i);
1931 /* this does not change the status of this regionview w.r.t the editor
1936 SelectionCleared (this); /* EMIT SIGNAL */
1941 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1943 clear_selection_except (ev);
1945 /* don't bother with checking to see if we should remove this
1946 regionview from the editor selection, since we're about to add
1947 another note, and thus put/keep this regionview in the editor
1951 if (!ev->selected()) {
1952 add_to_selection (ev);
1957 MidiRegionView::select_all_notes ()
1961 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1962 add_to_selection (*i);
1967 MidiRegionView::select_range (framepos_t start, framepos_t end)
1971 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1972 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
1973 if (t >= start && t <= end) {
1974 add_to_selection (*i);
1980 MidiRegionView::invert_selection ()
1982 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1983 if ((*i)->selected()) {
1984 remove_from_selection(*i);
1986 add_to_selection (*i);
1992 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1994 uint8_t low_note = 127;
1995 uint8_t high_note = 0;
1996 MidiModel::Notes& notes (_model->notes());
1997 _optimization_iterator = _events.begin();
2003 if (extend && _selection.empty()) {
2009 /* scan existing selection to get note range */
2011 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2012 if ((*i)->note()->note() < low_note) {
2013 low_note = (*i)->note()->note();
2015 if ((*i)->note()->note() > high_note) {
2016 high_note = (*i)->note()->note();
2020 low_note = min (low_note, notenum);
2021 high_note = max (high_note, notenum);
2024 _no_sound_notes = true;
2026 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2028 boost::shared_ptr<NoteType> note (*n);
2029 CanvasNoteEvent* cne;
2030 bool select = false;
2032 if (((1 << note->channel()) & channel_mask) != 0) {
2034 if ((note->note() >= low_note && note->note() <= high_note)) {
2037 } else if (note->note() == notenum) {
2043 if ((cne = find_canvas_note (note)) != 0) {
2044 // extend is false because we've taken care of it,
2045 // since it extends by time range, not pitch.
2046 note_selected (cne, add, false);
2050 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2054 _no_sound_notes = false;
2058 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2060 MidiModel::Notes& notes (_model->notes());
2061 _optimization_iterator = _events.begin();
2063 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2065 boost::shared_ptr<NoteType> note (*n);
2066 CanvasNoteEvent* cne;
2068 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2069 if ((cne = find_canvas_note (note)) != 0) {
2070 if (cne->selected()) {
2071 note_deselected (cne);
2073 note_selected (cne, true, false);
2081 MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2084 clear_selection_except (ev);
2085 if (!_selection.empty()) {
2086 PublicEditor& editor (trackview.editor());
2087 editor.get_selection().add (this);
2093 if (!ev->selected()) {
2094 add_to_selection (ev);
2098 /* find end of latest note selected, select all between that and the start of "ev" */
2100 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2101 Evoral::MusicalTime latest = 0;
2103 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2104 if ((*i)->note()->end_time() > latest) {
2105 latest = (*i)->note()->end_time();
2107 if ((*i)->note()->time() < earliest) {
2108 earliest = (*i)->note()->time();
2112 if (ev->note()->end_time() > latest) {
2113 latest = ev->note()->end_time();
2116 if (ev->note()->time() < earliest) {
2117 earliest = ev->note()->time();
2120 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2122 /* find notes entirely within OR spanning the earliest..latest range */
2124 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2125 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2126 add_to_selection (*i);
2134 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2136 remove_from_selection (ev);
2140 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
2150 // TODO: Make this faster by storing the last updated selection rect, and only
2151 // adjusting things that are in the area that appears/disappeared.
2152 // We probably need a tree to be able to find events in O(log(n)) time.
2154 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2156 /* check if any corner of the note is inside the rect
2159 1) this is computing "touched by", not "contained by" the rect.
2160 2) this does not require that events be sorted in time.
2163 const double ix1 = (*i)->x1();
2164 const double ix2 = (*i)->x2();
2165 const double iy1 = (*i)->y1();
2166 const double iy2 = (*i)->y2();
2168 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2169 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2170 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2171 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2174 if (!(*i)->selected()) {
2175 add_to_selection (*i);
2177 } else if ((*i)->selected() && !extend) {
2178 // Not inside rectangle
2179 remove_from_selection (*i);
2185 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2187 Selection::iterator i = _selection.find (ev);
2189 if (i != _selection.end()) {
2190 _selection.erase (i);
2193 ev->set_selected (false);
2194 ev->hide_velocity ();
2196 if (_selection.empty()) {
2197 PublicEditor& editor (trackview.editor());
2198 editor.get_selection().remove (this);
2203 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2205 bool add_mrv_selection = false;
2207 if (_selection.empty()) {
2208 add_mrv_selection = true;
2211 if (_selection.insert (ev).second) {
2212 ev->set_selected (true);
2213 play_midi_note ((ev)->note());
2216 if (add_mrv_selection) {
2217 PublicEditor& editor (trackview.editor());
2218 editor.get_selection().add (this);
2223 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2225 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2226 PossibleChord to_play;
2227 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2229 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2230 if ((*i)->note()->time() < earliest) {
2231 earliest = (*i)->note()->time();
2235 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2236 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2237 to_play.push_back ((*i)->note());
2239 (*i)->move_event(dx, dy);
2242 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2244 if (to_play.size() > 1) {
2246 PossibleChord shifted;
2248 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2249 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2250 moved_note->set_note (moved_note->note() + cumulative_dy);
2251 shifted.push_back (moved_note);
2254 play_midi_chord (shifted);
2256 } else if (!to_play.empty()) {
2258 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2259 moved_note->set_note (moved_note->note() + cumulative_dy);
2260 play_midi_note (moved_note);
2266 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2268 assert (!_selection.empty());
2270 uint8_t lowest_note_in_selection = 127;
2271 uint8_t highest_note_in_selection = 0;
2272 uint8_t highest_note_difference = 0;
2274 // find highest and lowest notes first
2276 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2277 uint8_t pitch = (*i)->note()->note();
2278 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2279 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2283 cerr << "dnote: " << (int) dnote << endl;
2284 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2285 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2286 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2287 << int(highest_note_in_selection) << endl;
2288 cerr << "selection size: " << _selection.size() << endl;
2289 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2292 // Make sure the note pitch does not exceed the MIDI standard range
2293 if (highest_note_in_selection + dnote > 127) {
2294 highest_note_difference = highest_note_in_selection - 127;
2297 start_note_diff_command (_("move notes"));
2299 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2301 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (source_beats_to_absolute_frames ((*i)->note()->time()) + dt);
2307 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2309 uint8_t original_pitch = (*i)->note()->note();
2310 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2312 // keep notes in standard midi range
2313 clamp_to_0_127(new_pitch);
2315 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2316 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2318 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2323 // care about notes being moved beyond the upper/lower bounds on the canvas
2324 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2325 highest_note_in_selection > midi_stream_view()->highest_note()) {
2326 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2330 /** @param x Pixel relative to the region position.
2331 * @return Snapped frame relative to the region position.
2334 MidiRegionView::snap_pixel_to_frame(double x)
2336 PublicEditor& editor (trackview.editor());
2337 return snap_frame_to_frame (editor.pixel_to_frame (x));
2340 /** @param x Pixel relative to the region position.
2341 * @return Snapped pixel relative to the region position.
2344 MidiRegionView::snap_to_pixel(double x)
2346 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2350 MidiRegionView::get_position_pixels()
2352 framepos_t region_frame = get_position();
2353 return trackview.editor().frame_to_pixel(region_frame);
2357 MidiRegionView::get_end_position_pixels()
2359 framepos_t frame = get_position() + get_duration ();
2360 return trackview.editor().frame_to_pixel(frame);
2364 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2366 /* the time converter will return the frame corresponding to `beats'
2367 relative to the start of the source. The start of the source
2368 is an implied position given by region->position - region->start
2370 const framepos_t source_start = _region->position() - _region->start();
2371 return source_start + _source_relative_time_converter.to (beats);
2375 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2377 /* the `frames' argument needs to be converted into a frame count
2378 relative to the start of the source before being passed in to the
2381 const framepos_t source_start = _region->position() - _region->start();
2382 return _source_relative_time_converter.from (frames - source_start);
2386 MidiRegionView::region_beats_to_region_frames(double beats) const
2388 return _region_relative_time_converter.to(beats);
2392 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2394 return _region_relative_time_converter.from(frames);
2398 MidiRegionView::begin_resizing (bool /*at_front*/)
2400 _resize_data.clear();
2402 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2403 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2405 // only insert CanvasNotes into the map
2407 NoteResizeData *resize_data = new NoteResizeData();
2408 resize_data->canvas_note = note;
2410 // create a new SimpleRect from the note which will be the resize preview
2411 SimpleRect *resize_rect = new SimpleRect(
2412 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2414 // calculate the colors: get the color settings
2415 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2416 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2419 // make the resize preview notes more transparent and bright
2420 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2422 // calculate color based on note velocity
2423 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2424 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2428 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2429 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2431 resize_data->resize_rect = resize_rect;
2432 _resize_data.push_back(resize_data);
2437 /** Update resizing notes while user drags.
2438 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2439 * @param at_front which end of the note (true == note on, false == note off)
2440 * @param delta_x change in mouse position since the start of the drag
2441 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2442 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2443 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2444 * as the \a primary note.
2447 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2449 bool cursor_set = false;
2451 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2452 SimpleRect* resize_rect = (*i)->resize_rect;
2453 CanvasNote* canvas_note = (*i)->canvas_note;
2458 current_x = canvas_note->x1() + delta_x;
2460 current_x = primary->x1() + delta_x;
2464 current_x = canvas_note->x2() + delta_x;
2466 current_x = primary->x2() + delta_x;
2471 resize_rect->property_x1() = snap_to_pixel(current_x);
2472 resize_rect->property_x2() = canvas_note->x2();
2474 resize_rect->property_x2() = snap_to_pixel(current_x);
2475 resize_rect->property_x1() = canvas_note->x1();
2481 beats = snap_pixel_to_frame (current_x);
2482 beats = region_frames_to_region_beats (beats);
2487 if (beats < canvas_note->note()->end_time()) {
2488 len = canvas_note->note()->time() - beats;
2489 len += canvas_note->note()->length();
2494 if (beats >= canvas_note->note()->time()) {
2495 len = beats - canvas_note->note()->time();
2502 snprintf (buf, sizeof (buf), "%.3g beats", len);
2503 show_verbose_cursor (buf, 0, 0);
2512 /** Finish resizing notes when the user releases the mouse button.
2513 * Parameters the same as for \a update_resizing().
2516 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2518 start_note_diff_command (_("resize notes"));
2520 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2521 CanvasNote* canvas_note = (*i)->canvas_note;
2522 SimpleRect* resize_rect = (*i)->resize_rect;
2524 /* Get the new x position for this resize, which is in pixels relative
2525 * to the region position.
2532 current_x = canvas_note->x1() + delta_x;
2534 current_x = primary->x1() + delta_x;
2538 current_x = canvas_note->x2() + delta_x;
2540 current_x = primary->x2() + delta_x;
2544 /* Convert that to a frame within the source */
2545 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2547 /* and then to beats */
2548 current_x = region_frames_to_region_beats (current_x);
2550 if (at_front && current_x < canvas_note->note()->end_time()) {
2551 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2553 double len = canvas_note->note()->time() - current_x;
2554 len += canvas_note->note()->length();
2557 /* XXX convert to beats */
2558 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2563 double len = current_x - canvas_note->note()->time();
2566 /* XXX convert to beats */
2567 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2575 _resize_data.clear();
2580 MidiRegionView::abort_resizing ()
2582 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2583 delete (*i)->resize_rect;
2587 _resize_data.clear ();
2591 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2593 uint8_t new_velocity;
2596 new_velocity = event->note()->velocity() + velocity;
2597 clamp_to_0_127(new_velocity);
2599 new_velocity = velocity;
2602 event->set_selected (event->selected()); // change color
2604 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2608 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2613 new_note = event->note()->note() + note;
2618 clamp_to_0_127 (new_note);
2619 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2623 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2625 bool change_start = false;
2626 bool change_length = false;
2627 Evoral::MusicalTime new_start = 0;
2628 Evoral::MusicalTime new_length = 0;
2630 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2632 front_delta: if positive - move the start of the note later in time (shortening it)
2633 if negative - move the start of the note earlier in time (lengthening it)
2635 end_delta: if positive - move the end of the note later in time (lengthening it)
2636 if negative - move the end of the note earlier in time (shortening it)
2640 if (front_delta < 0) {
2642 if (event->note()->time() < -front_delta) {
2645 new_start = event->note()->time() + front_delta; // moves earlier
2648 /* start moved toward zero, so move the end point out to where it used to be.
2649 Note that front_delta is negative, so this increases the length.
2652 new_length = event->note()->length() - front_delta;
2653 change_start = true;
2654 change_length = true;
2658 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2660 if (new_pos < event->note()->end_time()) {
2661 new_start = event->note()->time() + front_delta;
2662 /* start moved toward the end, so move the end point back to where it used to be */
2663 new_length = event->note()->length() - front_delta;
2664 change_start = true;
2665 change_length = true;
2672 bool can_change = true;
2673 if (end_delta < 0) {
2674 if (event->note()->length() < -end_delta) {
2680 new_length = event->note()->length() + end_delta;
2681 change_length = true;
2686 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2689 if (change_length) {
2690 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2695 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2697 uint8_t new_channel;
2701 if (event->note()->channel() < -chn) {
2704 new_channel = event->note()->channel() + chn;
2707 new_channel = event->note()->channel() + chn;
2710 new_channel = (uint8_t) chn;
2713 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2717 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2719 Evoral::MusicalTime new_time;
2723 if (event->note()->time() < -delta) {
2726 new_time = event->note()->time() + delta;
2729 new_time = event->note()->time() + delta;
2735 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2739 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2741 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2745 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2749 if (_selection.empty()) {
2764 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2765 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2771 start_note_diff_command (_("change velocities"));
2773 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2774 Selection::iterator next = i;
2776 change_note_velocity (*i, delta, true);
2782 if (!_selection.empty()) {
2784 snprintf (buf, sizeof (buf), "Vel %d",
2785 (int) (*_selection.begin())->note()->velocity());
2786 show_verbose_cursor (buf, 10, 10);
2792 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2794 if (_selection.empty()) {
2811 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2813 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2817 if ((int8_t) (*i)->note()->note() + delta > 127) {
2824 start_note_diff_command (_("transpose"));
2826 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2827 Selection::iterator next = i;
2829 change_note_note (*i, delta, true);
2837 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2843 /* grab the current grid distance */
2845 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2847 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2848 cerr << "Grid type not available as beats - TO BE FIXED\n";
2858 start_note_diff_command (_("change note lengths"));
2860 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2861 Selection::iterator next = i;
2864 /* note the negation of the delta for start */
2866 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2875 MidiRegionView::nudge_notes (bool forward)
2877 if (_selection.empty()) {
2881 /* pick a note as the point along the timeline to get the nudge distance.
2882 its not necessarily the earliest note, so we may want to pull the notes out
2883 into a vector and sort before using the first one.
2886 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
2888 framepos_t distance;
2890 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2892 /* grid is off - use nudge distance */
2894 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2900 framepos_t next_pos = ref_point;
2903 if (max_framepos - 1 < next_pos) {
2907 if (next_pos == 0) {
2913 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2914 distance = ref_point - next_pos;
2917 if (distance == 0) {
2921 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
2927 start_note_diff_command (_("nudge"));
2929 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2930 Selection::iterator next = i;
2932 change_note_time (*i, delta, true);
2940 MidiRegionView::change_channel(uint8_t channel)
2942 start_note_diff_command(_("change channel"));
2943 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2944 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2952 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2954 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2956 _pre_enter_cursor = editor->get_canvas_cursor ();
2958 if (_mouse_state == SelectTouchDragging) {
2959 note_selected (ev, true);
2962 show_verbose_cursor (ev->note ());
2966 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2968 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2970 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2971 (*i)->hide_velocity ();
2974 editor->verbose_cursor()->hide ();
2976 if (_pre_enter_cursor) {
2977 editor->set_canvas_cursor (_pre_enter_cursor);
2978 _pre_enter_cursor = 0;
2983 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
2986 /* XXX should get patch name if we can */
2987 s << _("Bank:") << (ev->patch()->bank() + MIDI_BP_ZERO) << '\n' << _("Program:") << ((int) ev->patch()->program()) + MIDI_BP_ZERO << '\n' << _("Channel:") << ((int) ev->patch()->channel() + 1);
2988 show_verbose_cursor (s.str(), 10, 20);
2992 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
2994 trackview.editor().verbose_cursor()->hide ();
2998 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3000 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3002 if (x_fraction > 0.0 && x_fraction < 0.25) {
3003 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3004 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
3005 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3007 if (_pre_enter_cursor && can_set_cursor) {
3008 editor->set_canvas_cursor (_pre_enter_cursor);
3014 MidiRegionView::set_frame_color()
3018 TimeAxisViewItem::set_frame_color ();
3025 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3026 } else if (high_enough_for_name) {
3027 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3032 if (!rect_visible) {
3033 f = UINT_RGBA_CHANGE_A (f, 0);
3036 frame->property_fill_color_rgba() = f;
3040 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3044 case FilterChannels:
3045 _force_channel = -1;
3048 _force_channel = mask;
3049 mask = 0xFFFF; // Show all notes as active (below)
3052 // Update notes for selection
3053 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3054 (*i)->on_channel_selection_change(mask);
3057 _last_channel_selection = mask;
3059 _patch_changes.clear ();
3060 display_patch_changes ();
3064 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
3066 _model_name = model;
3067 _custom_device_mode = custom_device_mode;
3072 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3074 if (_selection.empty()) {
3078 PublicEditor& editor (trackview.editor());
3082 /* XXX what to do ? */
3086 editor.get_cut_buffer().add (selection_as_cut_buffer());
3094 start_note_diff_command();
3096 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3103 note_diff_remove_note (*i);
3113 MidiRegionView::selection_as_cut_buffer () const
3117 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3118 NoteType* n = (*i)->note().get();
3119 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3122 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3128 /** This method handles undo */
3130 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3136 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3138 trackview.session()->begin_reversible_command (_("paste"));
3140 start_note_diff_command (_("paste"));
3142 Evoral::MusicalTime beat_delta;
3143 Evoral::MusicalTime paste_pos_beats;
3144 Evoral::MusicalTime duration;
3145 Evoral::MusicalTime end_point = 0;
3147 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3148 paste_pos_beats = region_frames_to_region_beats (pos - _region->position());
3149 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3150 paste_pos_beats = 0;
3152 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",
3153 (*mcb.notes().begin())->time(),
3154 (*mcb.notes().rbegin())->end_time(),
3155 duration, pos, _region->position(),
3156 paste_pos_beats, beat_delta));
3160 for (int n = 0; n < (int) times; ++n) {
3162 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3164 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3165 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3167 /* make all newly added notes selected */
3169 note_diff_add_note (copied_note, true);
3170 end_point = copied_note->end_time();
3173 paste_pos_beats += duration;
3176 /* if we pasted past the current end of the region, extend the region */
3178 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3179 framepos_t region_end = _region->position() + _region->length() - 1;
3181 if (end_frame > region_end) {
3183 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3185 _region->clear_changes ();
3186 _region->set_length (end_frame);
3187 trackview.session()->add_command (new StatefulDiffCommand (_region));
3192 trackview.session()->commit_reversible_command ();
3195 struct EventNoteTimeEarlyFirstComparator {
3196 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3197 return a->note()->time() < b->note()->time();
3202 MidiRegionView::time_sort_events ()
3204 if (!_sort_needed) {
3208 EventNoteTimeEarlyFirstComparator cmp;
3211 _sort_needed = false;
3215 MidiRegionView::goto_next_note (bool add_to_selection)
3217 bool use_next = false;
3219 if (_events.back()->selected()) {
3223 time_sort_events ();
3225 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3226 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3228 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3229 if ((*i)->selected()) {
3232 } else if (use_next) {
3233 if (channel_mask & (1 << (*i)->note()->channel())) {
3234 if (!add_to_selection) {
3237 note_selected (*i, true, false);
3244 /* use the first one */
3246 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3247 unique_select (_events.front());
3252 MidiRegionView::goto_previous_note (bool add_to_selection)
3254 bool use_next = false;
3256 if (_events.front()->selected()) {
3260 time_sort_events ();
3262 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3263 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3265 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3266 if ((*i)->selected()) {
3269 } else if (use_next) {
3270 if (channel_mask & (1 << (*i)->note()->channel())) {
3271 if (!add_to_selection) {
3274 note_selected (*i, true, false);
3281 /* use the last one */
3283 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3284 unique_select (*(_events.rbegin()));
3289 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3291 bool had_selected = false;
3293 time_sort_events ();
3295 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3296 if ((*i)->selected()) {
3297 selected.insert ((*i)->note());
3298 had_selected = true;
3302 if (allow_all_if_none_selected && !had_selected) {
3303 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3304 selected.insert ((*i)->note());
3310 MidiRegionView::update_ghost_note (double x, double y)
3312 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3317 _note_group->w2i (x, y);
3319 PublicEditor& editor = trackview.editor ();
3321 framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3322 framecnt_t grid_frames;
3323 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3325 /* use region_frames... because we are converting a delta within the region
3328 double length = region_frames_to_region_beats (snap_frame_to_frame (f + grid_frames) - f);
3330 /* note that this sets the time of the ghost note in beats relative to
3331 the start of the source; that is how all note times are stored.
3333 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3334 _ghost_note->note()->set_length (length);
3335 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3336 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3338 /* the ghost note does not appear in ghost regions, so pass false in here */
3339 update_note (_ghost_note, false);
3341 show_verbose_cursor (_ghost_note->note ());
3345 MidiRegionView::create_ghost_note (double x, double y)
3347 remove_ghost_note ();
3349 boost::shared_ptr<NoteType> g (new NoteType);
3350 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3351 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3352 update_ghost_note (x, y);
3353 _ghost_note->show ();
3358 show_verbose_cursor (_ghost_note->note ());
3362 MidiRegionView::snap_changed ()
3368 create_ghost_note (_last_ghost_x, _last_ghost_y);
3372 MidiRegionView::drop_down_keys ()
3374 _mouse_state = None;
3378 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3380 double note = midi_stream_view()->y_to_note(y);
3382 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3384 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3386 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3387 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3388 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3389 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3394 bool add_mrv_selection = false;
3396 if (_selection.empty()) {
3397 add_mrv_selection = true;
3400 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3401 if (_selection.insert (*i).second) {
3402 (*i)->set_selected (true);
3406 if (add_mrv_selection) {
3407 PublicEditor& editor (trackview.editor());
3408 editor.get_selection().add (this);
3413 MidiRegionView::color_handler ()
3415 RegionView::color_handler ();
3417 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3418 (*i)->set_selected ((*i)->selected()); // will change color
3421 /* XXX probably more to do here */
3425 MidiRegionView::enable_display (bool yn)
3427 RegionView::enable_display (yn);
3434 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3436 if (_step_edit_cursor == 0) {
3437 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3439 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3440 _step_edit_cursor->property_y1() = 0;
3441 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3442 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3443 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3446 move_step_edit_cursor (pos);
3447 _step_edit_cursor->show ();
3451 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3453 _step_edit_cursor_position = pos;
3455 if (_step_edit_cursor) {
3456 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3457 _step_edit_cursor->property_x1() = pixel;
3458 set_step_edit_cursor_width (_step_edit_cursor_width);
3463 MidiRegionView::hide_step_edit_cursor ()
3465 if (_step_edit_cursor) {
3466 _step_edit_cursor->hide ();
3471 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3473 _step_edit_cursor_width = beats;
3475 if (_step_edit_cursor) {
3476 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3480 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3481 * @param w Source that the data will end up in.
3484 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3486 if (!_active_notes) {
3487 /* we aren't actively being recorded to */
3491 boost::shared_ptr<MidiSource> src = w.lock ();
3492 if (!src || src != midi_region()->midi_source()) {
3493 /* recorded data was not destined for our source */
3497 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3499 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3501 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3503 framepos_t back = max_framepos;
3505 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3506 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3507 assert (ev.buffer ());
3509 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3510 frames from the start of the source, and so time_beats is in terms of the
3514 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3516 if (ev.type() == MIDI_CMD_NOTE_ON) {
3518 boost::shared_ptr<NoteType> note (
3519 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3522 add_note (note, true);
3524 /* fix up our note range */
3525 if (ev.note() < _current_range_min) {
3526 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3527 } else if (ev.note() > _current_range_max) {
3528 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3531 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3532 resolve_note (ev.note (), time_beats);
3538 midi_stream_view()->check_record_layers (region(), back);
3542 MidiRegionView::trim_front_starting ()
3544 /* Reparent the note group to the region view's parent, so that it doesn't change
3545 when the region view is trimmed.
3547 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3548 _temporary_note_group->move (group->property_x(), group->property_y());
3549 _note_group->reparent (*_temporary_note_group);
3553 MidiRegionView::trim_front_ending ()
3555 _note_group->reparent (*group);
3556 delete _temporary_note_group;
3557 _temporary_note_group = 0;
3559 if (_region->start() < 0) {
3560 /* Trim drag made start time -ve; fix this */
3561 midi_region()->fix_negative_start ();
3566 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3568 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3569 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3573 change_patch_change (pc->patch(), d.patch ());
3578 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3581 snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3582 Evoral::midi_note_name (n->note()).c_str(),
3584 (int) n->channel() + 1,
3585 (int) n->velocity());
3587 show_verbose_cursor (buf, 10, 20);
3591 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3595 trackview.editor().get_pointer_position (wx, wy);
3600 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3602 double x1, y1, x2, y2;
3603 trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3605 if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3606 wy -= (y2 - y1) + 2 * yoffset;
3609 trackview.editor().verbose_cursor()->set (text, wx, wy);
3610 trackview.editor().verbose_cursor()->show ();
3613 /** @param p A session framepos.
3614 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3615 * @return p snapped to the grid subdivision underneath it.
3618 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3620 PublicEditor& editor = trackview.editor ();
3623 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3629 grid_frames = region_beats_to_region_frames (grid_beats);
3631 /* Hack so that we always snap to the note that we are over, instead of snapping
3632 to the next one if we're more than halfway through the one we're over.
3634 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3635 p -= grid_frames / 2;
3638 return snap_frame_to_frame (p);
3641 /** Called when the selection has been cleared in any MidiRegionView.
3642 * @param rv MidiRegionView that the selection was cleared in.
3645 MidiRegionView::selection_cleared (MidiRegionView* rv)
3651 /* Clear our selection in sympathy; but don't signal the fact */
3652 clear_selection (false);