2 Copyright (C) 2001-2007 Paul Davis
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-program-change.h"
53 #include "ghostregion.h"
54 #include "gui_thread.h"
56 #include "midi_cut_buffer.h"
57 #include "midi_list_editor.h"
58 #include "midi_region_view.h"
59 #include "midi_streamview.h"
60 #include "midi_time_axis.h"
61 #include "midi_time_axis.h"
62 #include "midi_util.h"
63 #include "note_player.h"
64 #include "public_editor.h"
65 #include "rgb_macros.h"
66 #include "selection.h"
67 #include "simpleline.h"
68 #include "streamview.h"
70 #include "mouse_cursors.h"
74 using namespace ARDOUR;
76 using namespace Editing;
77 using namespace ArdourCanvas;
78 using Gtkmm2ext::Keyboard;
80 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
81 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
82 : RegionView (parent, tv, r, spu, basic_color)
84 , _last_channel_selection(0xFFFF)
85 , _current_range_min(0)
86 , _current_range_max(0)
87 , _model_name(string())
88 , _custom_device_mode(string())
90 , _note_group(new ArdourCanvas::Group(*parent))
94 , _step_edit_cursor (0)
95 , _step_edit_cursor_width (1.0)
96 , _step_edit_cursor_position (0.0)
100 , _optimization_iterator (_events.end())
102 , no_sound_notes (false)
105 , pre_enter_cursor (0)
107 _note_group->raise_to_top();
108 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
110 connect_to_diskstream ();
113 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
114 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
115 TimeAxisViewItem::Visibility visibility)
116 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
118 , _last_channel_selection(0xFFFF)
119 , _model_name(string())
120 , _custom_device_mode(string())
122 , _note_group(new ArdourCanvas::Group(*parent))
126 , _step_edit_cursor (0)
127 , _step_edit_cursor_width (1.0)
128 , _step_edit_cursor_position (0.0)
131 , _sort_needed (true)
132 , _optimization_iterator (_events.end())
134 , no_sound_notes (false)
138 _note_group->raise_to_top();
139 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
141 connect_to_diskstream ();
144 MidiRegionView::MidiRegionView (const MidiRegionView& other)
145 : sigc::trackable(other)
148 , _last_channel_selection(0xFFFF)
149 , _model_name(string())
150 , _custom_device_mode(string())
152 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
156 , _step_edit_cursor (0)
157 , _step_edit_cursor_width (1.0)
158 , _step_edit_cursor_position (0.0)
161 , _sort_needed (true)
162 , _optimization_iterator (_events.end())
164 , no_sound_notes (false)
171 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
172 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
177 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
178 : RegionView (other, boost::shared_ptr<Region> (region))
180 , _last_channel_selection(0xFFFF)
181 , _model_name(string())
182 , _custom_device_mode(string())
184 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
188 , _step_edit_cursor (0)
189 , _step_edit_cursor_width (1.0)
190 , _step_edit_cursor_position (0.0)
193 , _sort_needed (true)
194 , _optimization_iterator (_events.end())
196 , no_sound_notes (false)
203 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
204 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
210 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
212 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
214 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
215 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
219 midi_region()->midi_source(0)->load_model();
222 _model = midi_region()->midi_source(0)->model();
223 _enable_display = false;
225 RegionView::init (basic_color, false);
227 compute_colors (basic_color);
229 set_height (trackview.current_height());
232 region_sync_changed ();
233 region_resized (ARDOUR::bounds_change);
236 reset_width_dependent_items (_pixel_width);
240 _enable_display = true;
243 display_model (_model);
247 group->raise_to_top();
248 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
250 midi_view()->signal_channel_mode_changed().connect(
251 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
253 midi_view()->signal_midi_patch_settings_changed().connect(
254 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
256 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
258 connect_to_diskstream ();
262 MidiRegionView::connect_to_diskstream ()
264 midi_view()->midi_track()->DataRecorded.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::data_recorded, this, _1, _2), gui_context ());
268 MidiRegionView::canvas_event(GdkEvent* ev)
271 case GDK_ENTER_NOTIFY:
272 case GDK_LEAVE_NOTIFY:
273 _last_event_x = ev->crossing.x;
274 _last_event_y = ev->crossing.y;
276 case GDK_MOTION_NOTIFY:
277 _last_event_x = ev->motion.x;
278 _last_event_y = ev->motion.y;
284 if (!trackview.editor().internal_editing()) {
288 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
289 to its items, which means that ev->type == GDK_SCROLL will never be seen
294 return scroll (&ev->scroll);
297 return key_press (&ev->key);
299 case GDK_KEY_RELEASE:
300 return key_release (&ev->key);
302 case GDK_BUTTON_PRESS:
303 return button_press (&ev->button);
305 case GDK_2BUTTON_PRESS:
308 case GDK_BUTTON_RELEASE:
309 return button_release (&ev->button);
311 case GDK_ENTER_NOTIFY:
312 return enter_notify (&ev->crossing);
314 case GDK_LEAVE_NOTIFY:
315 return leave_notify (&ev->crossing);
317 case GDK_MOTION_NOTIFY:
318 return motion (&ev->motion);
328 MidiRegionView::remove_ghost_note ()
335 MidiRegionView::enter_notify (GdkEventCrossing* ev)
337 trackview.editor().MouseModeChanged.connect (
338 _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
341 Keyboard::magic_widget_grab_focus();
344 if (trackview.editor().current_mouse_mode() == MouseRange) {
345 create_ghost_note (ev->x, ev->y);
352 MidiRegionView::leave_notify (GdkEventCrossing*)
354 _mouse_mode_connection.disconnect ();
356 trackview.editor().hide_verbose_canvas_cursor ();
357 remove_ghost_note ();
362 MidiRegionView::mouse_mode_changed ()
364 if (trackview.editor().current_mouse_mode() == MouseRange && trackview.editor().internal_editing()) {
365 create_ghost_note (_last_event_x, _last_event_y);
367 remove_ghost_note ();
368 trackview.editor().hide_verbose_canvas_cursor ();
373 MidiRegionView::button_press (GdkEventButton* ev)
377 group->w2i (_last_x, _last_y);
379 if (_mouse_state != SelectTouchDragging && ev->button == 1) {
380 _pressed_button = ev->button;
381 _mouse_state = Pressed;
385 _pressed_button = ev->button;
391 MidiRegionView::button_release (GdkEventButton* ev)
393 double event_x, event_y;
394 framepos_t event_frame = 0;
398 group->w2i(event_x, event_y);
399 group->ungrab(ev->time);
400 event_frame = trackview.editor().pixel_to_frame(event_x);
402 if (ev->button == 3) {
404 } else if (_pressed_button != 1) {
408 switch (_mouse_state) {
409 case Pressed: // Clicked
410 switch (trackview.editor().current_mouse_mode()) {
414 maybe_select_by_position (ev, event_x, event_y);
420 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
424 create_note_at (event_x, event_y, beats, true);
432 case SelectRectDragging: // Select drag done
438 case AddDragging: // Add drag done
440 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
441 const double x = _drag_rect->property_x1();
442 const double length = trackview.editor().pixel_to_frame
443 (_drag_rect->property_x2() - _drag_rect->property_x1());
445 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), true);
451 create_ghost_note (ev->x, ev->y);
461 MidiRegionView::motion (GdkEventMotion* ev)
463 double event_x, event_y;
464 framepos_t event_frame = 0;
468 group->w2i(event_x, event_y);
470 // convert event_x to global frame
471 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
472 trackview.editor().snap_to(event_frame);
473 // convert event_frame back to local coordinates relative to position
474 event_frame -= _region->position();
477 update_ghost_note (ev->x, ev->y);
480 /* any motion immediately hides velocity text that may have been visible */
482 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
483 (*i)->hide_velocity ();
486 switch (_mouse_state) {
487 case Pressed: // Maybe start a drag, if we've moved a bit
489 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
490 /* no appreciable movement since the button was pressed */
495 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
496 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
497 Gdk::Cursor(Gdk::FLEUR), ev->time);
500 _drag_start_x = event_x;
501 _drag_start_y = event_y;
503 _drag_rect = new ArdourCanvas::SimpleRect(*group);
504 _drag_rect->property_x1() = event_x;
505 _drag_rect->property_y1() = event_y;
506 _drag_rect->property_x2() = event_x;
507 _drag_rect->property_y2() = event_y;
508 _drag_rect->property_outline_what() = 0xFF;
509 _drag_rect->property_outline_color_rgba()
510 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
511 _drag_rect->property_fill_color_rgba()
512 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
514 _mouse_state = SelectRectDragging;
517 // Add note drag start
518 } else if (trackview.editor().internal_editing()) {
523 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
524 Gdk::Cursor(Gdk::FLEUR), ev->time);
527 _drag_start_x = event_x;
528 _drag_start_y = event_y;
530 _drag_rect = new ArdourCanvas::SimpleRect(*group);
531 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
533 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
534 midi_stream_view()->y_to_note(event_y));
535 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
536 _drag_rect->property_y2() = _drag_rect->property_y1()
537 + floor(midi_stream_view()->note_height());
538 _drag_rect->property_outline_what() = 0xFF;
539 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
540 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
542 _mouse_state = AddDragging;
548 case SelectRectDragging: // Select drag motion
549 case AddDragging: // Add note drag motion
553 GdkModifierType state;
554 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
559 if (_mouse_state == AddDragging)
560 event_x = trackview.editor().frame_to_pixel(event_frame);
563 if (event_x > _drag_start_x)
564 _drag_rect->property_x2() = event_x;
566 _drag_rect->property_x1() = event_x;
569 if (_drag_rect && _mouse_state == SelectRectDragging) {
570 if (event_y > _drag_start_y)
571 _drag_rect->property_y2() = event_y;
573 _drag_rect->property_y1() = event_y;
575 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
581 case SelectTouchDragging:
593 MidiRegionView::scroll (GdkEventScroll* ev)
595 if (_selection.empty()) {
599 trackview.editor().hide_verbose_canvas_cursor ();
601 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
603 if (ev->direction == GDK_SCROLL_UP) {
604 change_velocities (true, fine, false);
605 } else if (ev->direction == GDK_SCROLL_DOWN) {
606 change_velocities (false, fine, false);
612 MidiRegionView::key_press (GdkEventKey* ev)
614 /* since GTK bindings are generally activated on press, and since
615 detectable auto-repeat is the name of the game and only sends
616 repeated presses, carry out key actions at key press, not release.
619 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
620 _mouse_state = SelectTouchDragging;
623 } else if (ev->keyval == GDK_Escape) {
627 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
629 bool start = (ev->keyval == GDK_comma);
630 bool end = (ev->keyval == GDK_period);
631 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
632 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
634 change_note_lengths (fine, shorter, 0.0, start, end);
638 } else if (ev->keyval == GDK_Delete) {
643 } else if (ev->keyval == GDK_Tab) {
645 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
646 goto_previous_note ();
652 } else if (ev->keyval == GDK_Up) {
654 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
655 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
657 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
658 change_velocities (true, fine, allow_smush);
660 transpose (true, fine, allow_smush);
664 } else if (ev->keyval == GDK_Down) {
666 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
667 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
669 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
670 change_velocities (false, fine, allow_smush);
672 transpose (false, fine, allow_smush);
676 } else if (ev->keyval == GDK_Left) {
681 } else if (ev->keyval == GDK_Right) {
686 } else if (ev->keyval == GDK_Control_L) {
695 MidiRegionView::key_release (GdkEventKey* ev)
697 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
705 MidiRegionView::show_list_editor ()
708 _list_editor = new MidiListEditor (trackview.session(), midi_region());
710 _list_editor->present ();
713 /** Add a note to the model, and the view, at a canvas (click) coordinate.
714 * \param x horizontal position in pixels
715 * \param y vertical position in pixels
716 * \param length duration of the note in beats, which will be snapped to the grid
717 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
720 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
722 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
723 MidiStreamView* const view = mtv->midi_view();
725 double note = midi_stream_view()->y_to_note(y);
728 assert(note <= 127.0);
730 // Start of note in frames relative to region start
731 framepos_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
732 assert(start_frames >= 0);
735 length = frames_to_beats(
736 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
738 assert (length != 0);
741 length = frames_to_beats (beats_to_frames (length) - 1);
744 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
748 /* pick the highest selected channel, unless all channels are selected,
749 which is interpreted to mean channel 1 (zero)
752 for (uint16_t i = 0; i < 16; ++i) {
753 if (chn_mask & (1<<i)) {
763 const boost::shared_ptr<NoteType> new_note (new NoteType (channel,
764 frames_to_beats(start_frames + _region->start()), length,
765 (uint8_t)note, 0x40));
767 if (_model->contains (new_note)) {
771 view->update_note_range(new_note->note());
773 MidiModel::DiffCommand* cmd = _model->new_diff_command("add note");
775 _model->apply_command(*trackview.session(), cmd);
777 play_midi_note (new_note);
781 MidiRegionView::clear_events()
786 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
787 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
792 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
797 _pgm_changes.clear();
799 _optimization_iterator = _events.end();
803 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
807 content_connection.disconnect ();
808 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
812 if (_enable_display) {
818 MidiRegionView::start_diff_command(string name)
820 if (!_diff_command) {
821 _diff_command = _model->new_diff_command(name);
826 MidiRegionView::diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
829 _diff_command->add(note);
832 _marked_for_selection.insert(note);
835 _marked_for_velocity.insert(note);
840 MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
842 if (_diff_command && ev->note()) {
843 _diff_command->remove(ev->note());
848 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
849 MidiModel::DiffCommand::Property property,
853 _diff_command->change (ev->note(), property, val);
858 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
859 MidiModel::DiffCommand::Property property,
860 Evoral::MusicalTime val)
863 _diff_command->change (ev->note(), property, val);
868 MidiRegionView::apply_diff ()
872 if (!_diff_command) {
876 if ((add_or_remove = _diff_command->adds_or_removes())) {
877 // Mark all selected notes for selection when model reloads
878 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
879 _marked_for_selection.insert((*i)->note());
883 _model->apply_command(*trackview.session(), _diff_command);
885 midi_view()->midi_track()->playlist_modified();
888 _marked_for_selection.clear();
891 _marked_for_velocity.clear();
895 MidiRegionView::apply_diff_as_subcommand()
899 if (!_diff_command) {
903 if ((add_or_remove = _diff_command->adds_or_removes())) {
904 // Mark all selected notes for selection when model reloads
905 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
906 _marked_for_selection.insert((*i)->note());
910 _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
912 midi_view()->midi_track()->playlist_modified();
915 _marked_for_selection.clear();
917 _marked_for_velocity.clear();
922 MidiRegionView::abort_command()
924 delete _diff_command;
930 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
932 if (_optimization_iterator != _events.end()) {
933 ++_optimization_iterator;
936 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
937 return *_optimization_iterator;
940 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
941 if ((*_optimization_iterator)->note() == note) {
942 return *_optimization_iterator;
950 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
952 MidiModel::Notes notes;
953 _model->get_notes (notes, op, val, chan_mask);
955 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
956 CanvasNoteEvent* cne = find_canvas_note (*n);
964 MidiRegionView::redisplay_model()
966 // Don't redisplay the model if we're currently recording and displaying that
972 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
976 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
980 MidiModel::ReadLock lock(_model->read_lock());
982 MidiModel::Notes& notes (_model->notes());
983 _optimization_iterator = _events.begin();
985 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
987 boost::shared_ptr<NoteType> note (*n);
988 CanvasNoteEvent* cne;
991 if (note_in_region_range (note, visible)) {
993 if ((cne = find_canvas_note (note)) != 0) {
1000 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1002 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1014 add_note (note, visible);
1019 if ((cne = find_canvas_note (note)) != 0) {
1027 /* remove note items that are no longer valid */
1029 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1030 if (!(*i)->valid ()) {
1032 i = _events.erase (i);
1038 _pgm_changes.clear();
1042 display_program_changes();
1044 _marked_for_selection.clear ();
1045 _marked_for_velocity.clear ();
1047 /* we may have caused _events to contain things out of order (e.g. if a note
1048 moved earlier or later). we don't generally need them in time order, but
1049 make a note that a sort is required for those cases that require it.
1052 _sort_needed = true;
1056 MidiRegionView::display_program_changes()
1058 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1059 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1061 for (uint8_t i = 0; i < 16; ++i) {
1062 if (chn_mask & (1<<i)) {
1063 display_program_changes_on_channel (i);
1069 MidiRegionView::display_program_changes_on_channel(uint8_t channel)
1071 boost::shared_ptr<Evoral::Control> control =
1072 _model->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation, channel));
1078 Glib::Mutex::Lock lock (control->list()->lock());
1080 for (AutomationList::const_iterator event = control->list()->begin();
1081 event != control->list()->end(); ++event) {
1082 double event_time = (*event)->when;
1083 double program_number = floor((*event)->value + 0.5);
1085 // Get current value of bank select MSB at time of the program change
1086 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1087 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1089 if (msb_control != 0) {
1090 msb = uint8_t(floor(msb_control->get_double(true, event_time) + 0.5));
1093 // Get current value of bank select LSB at time of the program change
1094 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1095 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1097 if (lsb_control != 0) {
1098 lsb = uint8_t(floor(lsb_control->get_double(true, event_time) + 0.5));
1101 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
1103 boost::shared_ptr<MIDI::Name::Patch> patch =
1104 MIDI::Name::MidiPatchManager::instance().find_patch(
1105 _model_name, _custom_device_mode, channel, patch_key);
1107 PCEvent program_change(event_time, uint8_t(program_number), channel);
1110 add_pgm_change(program_change, patch->name());
1113 // program_number is zero-based: convert to one-based
1114 snprintf(buf, 4, "%d", int(program_number+1));
1115 add_pgm_change(program_change, buf);
1121 MidiRegionView::display_sysexes()
1123 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1124 Evoral::MusicalTime time = (*i)->time();
1129 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1130 str << int((*i)->buffer()[b]);
1131 if (b != (*i)->size() -1) {
1135 string text = str.str();
1137 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1139 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1141 double height = midi_stream_view()->contents_height();
1143 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1144 new CanvasSysEx(*this, *group, text, height, x, 1.0));
1146 // Show unless program change is beyond the region bounds
1147 if (time - _region->start() >= _region->length() || time < _region->start()) {
1153 _sys_exes.push_back(sysex);
1158 MidiRegionView::~MidiRegionView ()
1160 in_destructor = true;
1162 trackview.editor().hide_verbose_canvas_cursor ();
1164 note_delete_connection.disconnect ();
1166 delete _list_editor;
1168 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1170 if (_active_notes) {
1178 delete _diff_command;
1179 delete _step_edit_cursor;
1183 MidiRegionView::region_resized (const PropertyChange& what_changed)
1185 RegionView::region_resized(what_changed);
1187 if (what_changed.contains (ARDOUR::Properties::position)) {
1188 set_duration(_region->length(), 0);
1189 if (_enable_display) {
1196 MidiRegionView::reset_width_dependent_items (double pixel_width)
1198 RegionView::reset_width_dependent_items(pixel_width);
1199 assert(_pixel_width == pixel_width);
1201 if (_enable_display) {
1205 move_step_edit_cursor (_step_edit_cursor_position);
1206 set_step_edit_cursor_width (_step_edit_cursor_width);
1210 MidiRegionView::set_height (double height)
1212 static const double FUDGE = 2.0;
1213 const double old_height = _height;
1214 RegionView::set_height(height);
1215 _height = height - FUDGE;
1217 apply_note_range(midi_stream_view()->lowest_note(),
1218 midi_stream_view()->highest_note(),
1219 height != old_height + FUDGE);
1222 name_pixbuf->raise_to_top();
1225 for (PgmChanges::iterator x = _pgm_changes.begin(); x != _pgm_changes.end(); ++x) {
1226 (*x)->set_height (midi_stream_view()->contents_height());
1229 if (_step_edit_cursor) {
1230 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1235 /** Apply the current note range from the stream view
1236 * by repositioning/hiding notes as necessary
1239 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1241 if (!_enable_display) {
1245 if (!force && _current_range_min == min && _current_range_max == max) {
1249 _current_range_min = min;
1250 _current_range_max = max;
1252 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1253 CanvasNoteEvent* event = *i;
1254 boost::shared_ptr<NoteType> note (event->note());
1256 if (note->note() < _current_range_min ||
1257 note->note() > _current_range_max) {
1263 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1265 const double y1 = midi_stream_view()->note_to_y(note->note());
1266 const double y2 = y1 + floor(midi_stream_view()->note_height());
1268 cnote->property_y1() = y1;
1269 cnote->property_y2() = y2;
1271 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1273 const double diamond_size = update_hit (chit);
1275 chit->set_height (diamond_size);
1281 MidiRegionView::add_ghost (TimeAxisView& tv)
1285 double unit_position = _region->position () / samples_per_unit;
1286 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1287 MidiGhostRegion* ghost;
1289 if (mtv && mtv->midi_view()) {
1290 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1291 to allow having midi notes on top of note lines and waveforms.
1293 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1295 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1298 ghost->set_height ();
1299 ghost->set_duration (_region->length() / samples_per_unit);
1300 ghosts.push_back (ghost);
1302 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1303 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1304 ghost->add_note(note);
1308 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1314 /** Begin tracking note state for successive calls to add_event
1317 MidiRegionView::begin_write()
1319 assert(!_active_notes);
1320 _active_notes = new CanvasNote*[128];
1321 for (unsigned i=0; i < 128; ++i) {
1322 _active_notes[i] = 0;
1327 /** Destroy note state for add_event
1330 MidiRegionView::end_write()
1332 delete[] _active_notes;
1334 _marked_for_selection.clear();
1335 _marked_for_velocity.clear();
1339 /** Resolve an active MIDI note (while recording).
1342 MidiRegionView::resolve_note(uint8_t note, double end_time)
1344 if (midi_view()->note_mode() != Sustained) {
1348 if (_active_notes && _active_notes[note]) {
1349 const framepos_t end_time_frames = beats_to_frames(end_time) - _region->start();
1350 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1351 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1352 _active_notes[note] = 0;
1357 /** Extend active notes to rightmost edge of region (if length is changed)
1360 MidiRegionView::extend_active_notes()
1362 if (!_active_notes) {
1366 for (unsigned i=0; i < 128; ++i) {
1367 if (_active_notes[i]) {
1368 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1375 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1377 if (no_sound_notes || !trackview.editor().sound_notes()) {
1381 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1383 if (!route_ui || !route_ui->midi_track()) {
1387 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1393 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1395 if (no_sound_notes || !trackview.editor().sound_notes()) {
1399 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1401 if (!route_ui || !route_ui->midi_track()) {
1405 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1407 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1416 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1418 const framepos_t note_start_frames = beats_to_frames(note->time());
1420 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1421 (note_start_frames < _region->start());
1423 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1424 (note->note() <= midi_stream_view()->highest_note());
1430 MidiRegionView::update_note (CanvasNote* ev)
1432 boost::shared_ptr<NoteType> note = ev->note();
1434 const framepos_t note_start_frames = beats_to_frames(note->time());
1436 /* trim note display to not overlap the end of its region */
1437 const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1439 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1440 const double y1 = midi_stream_view()->note_to_y(note->note());
1441 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1443 ev->property_x1() = x;
1444 ev->property_y1() = y1;
1445 if (note->length() > 0) {
1446 ev->property_x2() = note_endpixel;
1448 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1450 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1452 if (note->length() == 0) {
1453 if (_active_notes) {
1454 assert(note->note() < 128);
1455 // If this note is already active there's a stuck note,
1456 // finish the old note rectangle
1457 if (_active_notes[note->note()]) {
1458 CanvasNote* const old_rect = _active_notes[note->note()];
1459 boost::shared_ptr<NoteType> old_note = old_rect->note();
1460 old_rect->property_x2() = x;
1461 old_rect->property_outline_what() = (guint32) 0xF;
1463 _active_notes[note->note()] = ev;
1465 /* outline all but right edge */
1466 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1468 /* outline all edges */
1469 ev->property_outline_what() = (guint32) 0xF;
1474 MidiRegionView::update_hit (CanvasHit* ev)
1476 boost::shared_ptr<NoteType> note = ev->note();
1478 const framepos_t note_start_frames = beats_to_frames(note->time());
1479 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1480 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1481 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1485 return diamond_size;
1488 /** Add a MIDI note to the view (with length).
1490 * If in sustained mode, notes with length 0 will be considered active
1491 * notes, and resolve_note should be called when the corresponding note off
1492 * event arrives, to properly display the note.
1495 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1497 CanvasNoteEvent* event = 0;
1499 assert(note->time() >= 0);
1500 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1502 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1504 if (midi_view()->note_mode() == Sustained) {
1506 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1508 update_note (ev_rect);
1512 MidiGhostRegion* gr;
1514 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1515 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1516 gr->add_note(ev_rect);
1520 } else if (midi_view()->note_mode() == Percussive) {
1522 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1524 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1526 update_hit (ev_diamond);
1535 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1536 note_selected(event, true);
1539 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1540 event->show_velocity();
1542 event->on_channel_selection_change(_last_channel_selection);
1543 _events.push_back(event);
1554 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1555 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1557 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1559 /* potentially extend region to hold new note */
1561 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1562 framepos_t region_end = _region->position() + _region->length() - 1;
1564 if (end_frame > region_end) {
1565 _region->set_length (end_frame - _region->position(), this);
1568 _marked_for_selection.clear ();
1571 start_diff_command (_("step add"));
1572 diff_add_note (new_note, true, false);
1575 // last_step_edit_note = new_note;
1579 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1581 change_note_lengths (false, false, beats, false, true);
1585 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1587 assert(program.time >= 0);
1589 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1590 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1592 double height = midi_stream_view()->contents_height();
1594 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1595 new CanvasProgramChange(*this, *group,
1600 _custom_device_mode,
1601 program.time, program.channel, program.value));
1603 // Show unless program change is beyond the region bounds
1604 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1610 _pgm_changes.push_back(pgm_change);
1614 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1616 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1617 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1619 if (msb_control != 0) {
1620 msb = int(msb_control->get_double(true, time));
1623 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1624 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1626 if (lsb_control != 0) {
1627 lsb = lsb_control->get_double(true, time);
1630 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1631 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1632 double program_number = -1.0;
1633 if (program_control != 0) {
1634 program_number = program_control->get_double(true, time);
1637 key.msb = (int) floor(msb + 0.5);
1638 key.lsb = (int) floor(lsb + 0.5);
1639 key.program_number = (int) floor(program_number + 0.5);
1640 assert(key.is_sane());
1645 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1647 // TODO: Get the real event here and alter them at the original times
1648 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1649 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1650 if (msb_control != 0) {
1651 msb_control->set_double(double(new_patch.msb), true, old_program.time);
1654 // TODO: Get the real event here and alter them at the original times
1655 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1656 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1657 if (lsb_control != 0) {
1658 lsb_control->set_double(double(new_patch.lsb), true, old_program.time);
1661 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1662 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1664 assert(program_control != 0);
1665 program_control->set_double(float(new_patch.program_number), true, old_program.time);
1667 _pgm_changes.clear ();
1668 display_program_changes (); // XXX would be nice to limit to just old_program.channel
1672 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1674 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1675 alter_program_change(program_change_event, new_patch);
1679 MidiRegionView::previous_program(CanvasProgramChange& program)
1681 if (program.program() < 127) {
1682 MIDI::Name::PatchPrimaryKey key;
1683 get_patch_key_at(program.event_time(), program.channel(), key);
1684 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1686 key.program_number++;
1687 alter_program_change(program_change_event, key);
1692 MidiRegionView::next_program(CanvasProgramChange& program)
1694 if (program.program() > 0) {
1695 MIDI::Name::PatchPrimaryKey key;
1696 get_patch_key_at(program.event_time(), program.channel(), key);
1697 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1699 key.program_number--;
1700 alter_program_change(program_change_event, key);
1705 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1707 if (_selection.empty()) {
1711 if (_selection.erase (cne) > 0) {
1712 cerr << "Erased a CNE from selection\n";
1717 MidiRegionView::delete_selection()
1719 if (_selection.empty()) {
1723 start_diff_command (_("delete selection"));
1725 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1726 if ((*i)->selected()) {
1727 _diff_command->remove((*i)->note());
1737 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1739 start_diff_command (_("delete note"));
1740 _diff_command->remove (n);
1743 trackview.editor().hide_verbose_canvas_cursor ();
1747 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1749 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1750 if ((*i)->selected() && (*i) != ev) {
1751 (*i)->set_selected(false);
1752 (*i)->hide_velocity();
1760 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1762 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1765 Selection::iterator tmp = i;
1768 (*i)->set_selected (false);
1769 _selection.erase (i);
1778 /* don't bother with removing this regionview from the editor selection,
1779 since we're about to add another note, and thus put/keep this
1780 regionview in the editor selection.
1783 if (!ev->selected()) {
1784 add_to_selection (ev);
1789 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1791 uint8_t low_note = 127;
1792 uint8_t high_note = 0;
1793 MidiModel::Notes& notes (_model->notes());
1794 _optimization_iterator = _events.begin();
1800 if (extend && _selection.empty()) {
1806 /* scan existing selection to get note range */
1808 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1809 if ((*i)->note()->note() < low_note) {
1810 low_note = (*i)->note()->note();
1812 if ((*i)->note()->note() > high_note) {
1813 high_note = (*i)->note()->note();
1817 low_note = min (low_note, notenum);
1818 high_note = max (high_note, notenum);
1821 no_sound_notes = true;
1823 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1825 boost::shared_ptr<NoteType> note (*n);
1826 CanvasNoteEvent* cne;
1827 bool select = false;
1829 if (((1 << note->channel()) & channel_mask) != 0) {
1831 if ((note->note() >= low_note && note->note() <= high_note)) {
1834 } else if (note->note() == notenum) {
1840 if ((cne = find_canvas_note (note)) != 0) {
1841 // extend is false because we've taken care of it,
1842 // since it extends by time range, not pitch.
1843 note_selected (cne, add, false);
1847 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1851 no_sound_notes = false;
1855 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1857 MidiModel::Notes& notes (_model->notes());
1858 _optimization_iterator = _events.begin();
1860 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1862 boost::shared_ptr<NoteType> note (*n);
1863 CanvasNoteEvent* cne;
1865 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1866 if ((cne = find_canvas_note (note)) != 0) {
1867 if (cne->selected()) {
1868 note_deselected (cne);
1870 note_selected (cne, true, false);
1878 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1881 clear_selection_except(ev);
1886 if (!ev->selected()) {
1887 add_to_selection (ev);
1891 /* find end of latest note selected, select all between that and the start of "ev" */
1893 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1894 Evoral::MusicalTime latest = 0;
1896 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1897 if ((*i)->note()->end_time() > latest) {
1898 latest = (*i)->note()->end_time();
1900 if ((*i)->note()->time() < earliest) {
1901 earliest = (*i)->note()->time();
1905 if (ev->note()->end_time() > latest) {
1906 latest = ev->note()->end_time();
1909 if (ev->note()->time() < earliest) {
1910 earliest = ev->note()->time();
1913 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1915 /* find notes entirely within OR spanning the earliest..latest range */
1917 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1918 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1919 add_to_selection (*i);
1923 /* if events were guaranteed to be time sorted, we could do this.
1924 but as of sept 10th 2009, they no longer are.
1927 if ((*i)->note()->time() > latest) {
1936 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1938 remove_from_selection (ev);
1942 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1952 // TODO: Make this faster by storing the last updated selection rect, and only
1953 // adjusting things that are in the area that appears/disappeared.
1954 // We probably need a tree to be able to find events in O(log(n)) time.
1956 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1958 /* check if any corner of the note is inside the rect
1961 1) this is computing "touched by", not "contained by" the rect.
1962 2) this does not require that events be sorted in time.
1965 const double ix1 = (*i)->x1();
1966 const double ix2 = (*i)->x2();
1967 const double iy1 = (*i)->y1();
1968 const double iy2 = (*i)->y2();
1970 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1971 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1972 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1973 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1976 if (!(*i)->selected()) {
1977 add_to_selection (*i);
1979 } else if ((*i)->selected()) {
1980 // Not inside rectangle
1981 remove_from_selection (*i);
1987 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1989 Selection::iterator i = _selection.find (ev);
1991 if (i != _selection.end()) {
1992 _selection.erase (i);
1995 ev->set_selected (false);
1996 ev->hide_velocity ();
1998 if (_selection.empty()) {
1999 PublicEditor& editor (trackview.editor());
2000 editor.get_selection().remove (this);
2005 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2007 bool add_mrv_selection = false;
2009 if (_selection.empty()) {
2010 add_mrv_selection = true;
2013 if (_selection.insert (ev).second) {
2014 ev->set_selected (true);
2015 play_midi_note ((ev)->note());
2018 if (add_mrv_selection) {
2019 PublicEditor& editor (trackview.editor());
2020 editor.get_selection().add (this);
2025 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2027 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2028 PossibleChord to_play;
2029 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2031 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2032 if ((*i)->note()->time() < earliest) {
2033 earliest = (*i)->note()->time();
2037 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2038 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2039 to_play.push_back ((*i)->note());
2041 (*i)->move_event(dx, dy);
2044 if (dy && !_selection.empty() && !no_sound_notes && trackview.editor().sound_notes()) {
2046 if (to_play.size() > 1) {
2048 PossibleChord shifted;
2050 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2051 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2052 moved_note->set_note (moved_note->note() + cumulative_dy);
2053 shifted.push_back (moved_note);
2056 play_midi_chord (shifted);
2058 } else if (!to_play.empty()) {
2060 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2061 moved_note->set_note (moved_note->note() + cumulative_dy);
2062 play_midi_note (moved_note);
2068 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2070 assert (!_selection.empty());
2072 uint8_t lowest_note_in_selection = 127;
2073 uint8_t highest_note_in_selection = 0;
2074 uint8_t highest_note_difference = 0;
2076 // find highest and lowest notes first
2078 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2079 uint8_t pitch = (*i)->note()->note();
2080 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2081 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2085 cerr << "dnote: " << (int) dnote << endl;
2086 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2087 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2088 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2089 << int(highest_note_in_selection) << endl;
2090 cerr << "selection size: " << _selection.size() << endl;
2091 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2094 // Make sure the note pitch does not exceed the MIDI standard range
2095 if (highest_note_in_selection + dnote > 127) {
2096 highest_note_difference = highest_note_in_selection - 127;
2099 start_diff_command(_("move notes"));
2101 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2103 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2109 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
2111 uint8_t original_pitch = (*i)->note()->note();
2112 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2114 // keep notes in standard midi range
2115 clamp_to_0_127(new_pitch);
2117 // keep original pitch if note is dragged outside valid midi range
2118 if ((original_pitch != 0 && new_pitch == 0)
2119 || (original_pitch != 127 && new_pitch == 127)) {
2120 new_pitch = original_pitch;
2123 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2124 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2126 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
2131 // care about notes being moved beyond the upper/lower bounds on the canvas
2132 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2133 highest_note_in_selection > midi_stream_view()->highest_note()) {
2134 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2139 MidiRegionView::snap_pixel_to_frame(double x)
2141 PublicEditor& editor = trackview.editor();
2142 // x is region relative, convert it to global absolute frames
2143 framepos_t frame = editor.pixel_to_frame(x) + _region->position();
2144 editor.snap_to(frame);
2145 return frame - _region->position(); // convert back to region relative
2149 MidiRegionView::snap_frame_to_frame(framepos_t x)
2151 PublicEditor& editor = trackview.editor();
2152 // x is region relative, convert it to global absolute frames
2153 framepos_t frame = x + _region->position();
2154 editor.snap_to(frame);
2155 return frame - _region->position(); // convert back to region relative
2159 MidiRegionView::snap_to_pixel(double x)
2161 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2165 MidiRegionView::get_position_pixels()
2167 framepos_t region_frame = get_position();
2168 return trackview.editor().frame_to_pixel(region_frame);
2172 MidiRegionView::get_end_position_pixels()
2174 framepos_t frame = get_position() + get_duration ();
2175 return trackview.editor().frame_to_pixel(frame);
2179 MidiRegionView::beats_to_frames(double beats) const
2181 return _time_converter.to(beats);
2185 MidiRegionView::frames_to_beats(framepos_t frames) const
2187 return _time_converter.from(frames);
2191 MidiRegionView::begin_resizing (bool /*at_front*/)
2193 _resize_data.clear();
2195 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2196 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2198 // only insert CanvasNotes into the map
2200 NoteResizeData *resize_data = new NoteResizeData();
2201 resize_data->canvas_note = note;
2203 // create a new SimpleRect from the note which will be the resize preview
2204 SimpleRect *resize_rect = new SimpleRect(
2205 *group, note->x1(), note->y1(), note->x2(), note->y2());
2207 // calculate the colors: get the color settings
2208 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2209 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2212 // make the resize preview notes more transparent and bright
2213 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2215 // calculate color based on note velocity
2216 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2217 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2221 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2222 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2224 resize_data->resize_rect = resize_rect;
2225 _resize_data.push_back(resize_data);
2230 /** Update resizing notes while user drags.
2231 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2232 * @param at_front which end of the note (true == note on, false == note off)
2233 * @param delta_x change in mouse position since the start of the drag
2234 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2235 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2236 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2237 * as the \a primary note.
2240 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2242 bool cursor_set = false;
2244 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2245 SimpleRect* resize_rect = (*i)->resize_rect;
2246 CanvasNote* canvas_note = (*i)->canvas_note;
2251 current_x = canvas_note->x1() + delta_x;
2253 current_x = primary->x1() + delta_x;
2257 current_x = canvas_note->x2() + delta_x;
2259 current_x = primary->x2() + delta_x;
2264 resize_rect->property_x1() = snap_to_pixel(current_x);
2265 resize_rect->property_x2() = canvas_note->x2();
2267 resize_rect->property_x2() = snap_to_pixel(current_x);
2268 resize_rect->property_x1() = canvas_note->x1();
2274 beats = snap_pixel_to_frame (current_x);
2275 beats = frames_to_beats (beats);
2280 if (beats < canvas_note->note()->end_time()) {
2281 len = canvas_note->note()->time() - beats;
2282 len += canvas_note->note()->length();
2287 if (beats >= canvas_note->note()->time()) {
2288 len = beats - canvas_note->note()->time();
2295 snprintf (buf, sizeof (buf), "%.3g beats", len);
2296 trackview.editor().show_verbose_canvas_cursor_with (buf);
2305 /** Finish resizing notes when the user releases the mouse button.
2306 * Parameters the same as for \a update_resizing().
2309 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2311 start_diff_command(_("resize notes"));
2313 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2314 CanvasNote* canvas_note = (*i)->canvas_note;
2315 SimpleRect* resize_rect = (*i)->resize_rect;
2320 current_x = canvas_note->x1() + delta_x;
2322 current_x = primary->x1() + delta_x;
2326 current_x = canvas_note->x2() + delta_x;
2328 current_x = primary->x2() + delta_x;
2332 current_x = snap_pixel_to_frame (current_x);
2333 current_x = frames_to_beats (current_x);
2335 if (at_front && current_x < canvas_note->note()->end_time()) {
2336 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2338 double len = canvas_note->note()->time() - current_x;
2339 len += canvas_note->note()->length();
2342 /* XXX convert to beats */
2343 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2348 double len = current_x - canvas_note->note()->time();
2351 /* XXX convert to beats */
2352 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2360 _resize_data.clear();
2365 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2367 diff_add_change (event, MidiModel::DiffCommand::Channel, (uint8_t) channel);
2371 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2373 uint8_t new_velocity;
2376 new_velocity = event->note()->velocity() + velocity;
2377 clamp_to_0_127(new_velocity);
2379 new_velocity = velocity;
2382 event->set_selected (event->selected()); // change color
2384 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2388 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2393 new_note = event->note()->note() + note;
2398 clamp_to_0_127 (new_note);
2399 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2403 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2405 bool change_start = false;
2406 bool change_length = false;
2407 Evoral::MusicalTime new_start = 0;
2408 Evoral::MusicalTime new_length = 0;
2410 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2412 front_delta: if positive - move the start of the note later in time (shortening it)
2413 if negative - move the start of the note earlier in time (lengthening it)
2415 end_delta: if positive - move the end of the note later in time (lengthening it)
2416 if negative - move the end of the note earlier in time (shortening it)
2420 if (front_delta < 0) {
2422 if (event->note()->time() < -front_delta) {
2425 new_start = event->note()->time() + front_delta; // moves earlier
2428 /* start moved toward zero, so move the end point out to where it used to be.
2429 Note that front_delta is negative, so this increases the length.
2432 new_length = event->note()->length() - front_delta;
2433 change_start = true;
2434 change_length = true;
2438 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2440 if (new_pos < event->note()->end_time()) {
2441 new_start = event->note()->time() + front_delta;
2442 /* start moved toward the end, so move the end point back to where it used to be */
2443 new_length = event->note()->length() - front_delta;
2444 change_start = true;
2445 change_length = true;
2452 bool can_change = true;
2453 if (end_delta < 0) {
2454 if (event->note()->length() < -end_delta) {
2460 new_length = event->note()->length() + end_delta;
2461 change_length = true;
2466 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2469 if (change_length) {
2470 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2475 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2477 Evoral::MusicalTime new_time;
2481 if (event->note()->time() < -delta) {
2484 new_time = event->note()->time() + delta;
2487 new_time = event->note()->time() + delta;
2493 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2497 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2499 diff_add_change (event, MidiModel::DiffCommand::Length, t);
2503 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2507 if (_selection.empty()) {
2522 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2523 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2529 start_diff_command(_("change velocities"));
2531 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2532 Selection::iterator next = i;
2534 change_note_velocity (*i, delta, true);
2540 if (!_selection.empty()) {
2542 snprintf (buf, sizeof (buf), "Vel %d",
2543 (int) (*_selection.begin())->note()->velocity());
2544 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 10);
2550 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2552 if (_selection.empty()) {
2569 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2571 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2575 if ((int8_t) (*i)->note()->note() + delta > 127) {
2582 start_diff_command (_("transpose"));
2584 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2585 Selection::iterator next = i;
2587 change_note_note (*i, delta, true);
2595 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2601 /* grab the current grid distance */
2603 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2605 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2606 cerr << "Grid type not available as beats - TO BE FIXED\n";
2616 start_diff_command (_("change note lengths"));
2618 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2619 Selection::iterator next = i;
2622 /* note the negation of the delta for start */
2624 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2633 MidiRegionView::nudge_notes (bool forward)
2635 if (_selection.empty()) {
2639 /* pick a note as the point along the timeline to get the nudge distance.
2640 its not necessarily the earliest note, so we may want to pull the notes out
2641 into a vector and sort before using the first one.
2644 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2646 framepos_t distance;
2648 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2650 /* grid is off - use nudge distance */
2652 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2658 framepos_t next_pos = ref_point;
2661 if (max_framepos - 1 < next_pos) {
2665 if (next_pos == 0) {
2671 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2672 distance = ref_point - next_pos;
2675 if (distance == 0) {
2679 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2685 start_diff_command (_("nudge"));
2687 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2688 Selection::iterator next = i;
2690 change_note_time (*i, delta, true);
2698 MidiRegionView::change_channel(uint8_t channel)
2700 start_diff_command(_("change channel"));
2701 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2702 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2710 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2712 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2714 pre_enter_cursor = editor->get_canvas_cursor ();
2716 if (_mouse_state == SelectTouchDragging) {
2717 note_selected (ev, true);
2720 show_verbose_canvas_cursor (ev->note ());
2724 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2726 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2728 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2729 (*i)->hide_velocity ();
2732 editor->hide_verbose_canvas_cursor ();
2734 if (pre_enter_cursor) {
2735 editor->set_canvas_cursor (pre_enter_cursor);
2736 pre_enter_cursor = 0;
2741 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
2743 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2745 if (x_fraction > 0.0 && x_fraction < 0.25) {
2746 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
2747 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
2748 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
2750 if (pre_enter_cursor && can_set_cursor) {
2751 editor->set_canvas_cursor (pre_enter_cursor);
2757 MidiRegionView::set_frame_color()
2760 if (_selected && should_show_selection) {
2761 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2763 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2769 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2773 case FilterChannels:
2774 _force_channel = -1;
2777 _force_channel = mask;
2778 mask = 0xFFFF; // Show all notes as active (below)
2781 // Update notes for selection
2782 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2783 (*i)->on_channel_selection_change(mask);
2786 _last_channel_selection = mask;
2790 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2792 _model_name = model;
2793 _custom_device_mode = custom_device_mode;
2798 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2800 if (_selection.empty()) {
2804 PublicEditor& editor (trackview.editor());
2809 editor.get_cut_buffer().add (selection_as_cut_buffer());
2817 start_diff_command();
2819 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2825 diff_remove_note (*i);
2835 MidiRegionView::selection_as_cut_buffer () const
2839 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2840 NoteType* n = (*i)->note().get();
2841 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2844 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2851 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
2857 start_diff_command (_("paste"));
2859 Evoral::MusicalTime beat_delta;
2860 Evoral::MusicalTime paste_pos_beats;
2861 Evoral::MusicalTime duration;
2862 Evoral::MusicalTime end_point = 0;
2864 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2865 paste_pos_beats = frames_to_beats (pos - _region->position());
2866 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2867 paste_pos_beats = 0;
2871 for (int n = 0; n < (int) times; ++n) {
2873 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2875 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2876 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2878 /* make all newly added notes selected */
2880 diff_add_note (copied_note, true);
2881 end_point = copied_note->end_time();
2884 paste_pos_beats += duration;
2887 /* if we pasted past the current end of the region, extend the region */
2889 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
2890 framepos_t region_end = _region->position() + _region->length() - 1;
2892 if (end_frame > region_end) {
2894 trackview.session()->begin_reversible_command (_("paste"));
2896 _region->clear_changes ();
2897 _region->set_length (end_frame, this);
2898 trackview.session()->add_command (new StatefulDiffCommand (_region));
2904 struct EventNoteTimeEarlyFirstComparator {
2905 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2906 return a->note()->time() < b->note()->time();
2911 MidiRegionView::time_sort_events ()
2913 if (!_sort_needed) {
2917 EventNoteTimeEarlyFirstComparator cmp;
2920 _sort_needed = false;
2924 MidiRegionView::goto_next_note ()
2926 // framepos_t pos = -1;
2927 bool use_next = false;
2929 if (_events.back()->selected()) {
2933 time_sort_events ();
2935 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2936 if ((*i)->selected()) {
2939 } else if (use_next) {
2941 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2946 /* use the first one */
2948 unique_select (_events.front());
2953 MidiRegionView::goto_previous_note ()
2955 // framepos_t pos = -1;
2956 bool use_next = false;
2958 if (_events.front()->selected()) {
2962 time_sort_events ();
2964 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2965 if ((*i)->selected()) {
2968 } else if (use_next) {
2970 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2975 /* use the last one */
2977 unique_select (*(_events.rbegin()));
2981 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2983 bool had_selected = false;
2985 time_sort_events ();
2987 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2988 if ((*i)->selected()) {
2989 selected.insert ((*i)->note());
2990 had_selected = true;
2994 if (allow_all_if_none_selected && !had_selected) {
2995 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2996 selected.insert ((*i)->note());
3002 MidiRegionView::update_ghost_note (double x, double y)
3008 framepos_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
3009 trackview.editor().snap_to (f);
3010 f -= _region->position ();
3013 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
3018 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
3020 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
3021 _ghost_note->note()->set_length (length);
3022 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3024 update_note (_ghost_note);
3026 show_verbose_canvas_cursor (_ghost_note->note ());
3030 MidiRegionView::create_ghost_note (double x, double y)
3035 boost::shared_ptr<NoteType> g (new NoteType);
3036 _ghost_note = new NoEventCanvasNote (*this, *group, g);
3037 update_ghost_note (x, y);
3038 _ghost_note->show ();
3043 show_verbose_canvas_cursor (_ghost_note->note ());
3047 MidiRegionView::snap_changed ()
3053 create_ghost_note (_last_ghost_x, _last_ghost_y);
3057 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
3060 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
3061 Evoral::midi_note_name (n->note()).c_str(),
3063 (int) n->velocity());
3064 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 20);
3068 MidiRegionView::drop_down_keys ()
3070 _mouse_state = None;
3074 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3076 double note = midi_stream_view()->y_to_note(y);
3078 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3080 cerr << "Selecting by position\n";
3082 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3084 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3085 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3086 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3087 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3092 bool add_mrv_selection = false;
3094 if (_selection.empty()) {
3095 add_mrv_selection = true;
3098 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3099 if (_selection.insert (*i).second) {
3100 (*i)->set_selected (true);
3104 if (add_mrv_selection) {
3105 PublicEditor& editor (trackview.editor());
3106 editor.get_selection().add (this);
3111 MidiRegionView::color_handler ()
3113 RegionView::color_handler ();
3115 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3116 (*i)->set_selected ((*i)->selected()); // will change color
3119 /* XXX probably more to do here */
3123 MidiRegionView::enable_display (bool yn)
3125 RegionView::enable_display (yn);
3132 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3134 if (_step_edit_cursor == 0) {
3135 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3137 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3138 _step_edit_cursor->property_y1() = 0;
3139 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3140 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3141 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3144 move_step_edit_cursor (pos);
3145 _step_edit_cursor->show ();
3149 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3151 _step_edit_cursor_position = pos;
3153 if (_step_edit_cursor) {
3154 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3155 _step_edit_cursor->property_x1() = pixel;
3156 set_step_edit_cursor_width (_step_edit_cursor_width);
3161 MidiRegionView::hide_step_edit_cursor ()
3163 if (_step_edit_cursor) {
3164 _step_edit_cursor->hide ();
3169 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3171 _step_edit_cursor_width = beats;
3173 if (_step_edit_cursor) {
3174 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
3178 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3179 * @param buf Data that has been recorded.
3180 * @param w Source that this data will end up in.
3183 MidiRegionView::data_recorded (boost::shared_ptr<MidiBuffer> buf, boost::weak_ptr<MidiSource> w)
3185 if (!_active_notes) {
3186 /* we aren't actively being recorded to */
3190 boost::shared_ptr<MidiSource> src = w.lock ();
3191 if (!src || src != midi_region()->midi_source()) {
3192 /* recorded data was not destined for our source */
3196 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3197 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3199 framepos_t back = max_framepos;
3201 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3202 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3203 assert (ev.buffer ());
3205 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3207 if (ev.type() == MIDI_CMD_NOTE_ON) {
3209 boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > note (
3210 new Evoral::Note<Evoral::MusicalTime> (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3213 add_note (note, true);
3215 /* fix up our note range */
3216 if (ev.note() < _current_range_min) {
3217 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3218 } else if (ev.note() > _current_range_max) {
3219 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3222 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3223 resolve_note (ev.note (), time_beats);
3229 midi_stream_view()->check_record_layers (region(), back);