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"
52 #include "ghostregion.h"
53 #include "gui_thread.h"
55 #include "midi_cut_buffer.h"
56 #include "midi_list_editor.h"
57 #include "midi_region_view.h"
58 #include "midi_streamview.h"
59 #include "midi_time_axis.h"
60 #include "midi_time_axis.h"
61 #include "midi_util.h"
62 #include "public_editor.h"
63 #include "rgb_macros.h"
64 #include "selection.h"
65 #include "simpleline.h"
66 #include "streamview.h"
71 using namespace ARDOUR;
73 using namespace Editing;
74 using namespace ArdourCanvas;
75 using Gtkmm2ext::Keyboard;
77 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
78 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
79 : RegionView (parent, tv, r, spu, basic_color)
81 , _last_channel_selection(0xFFFF)
82 , _current_range_min(0)
83 , _current_range_max(0)
84 , _model_name(string())
85 , _custom_device_mode(string())
87 , _note_group(new ArdourCanvas::Group(*parent))
91 , _step_edit_cursor (0)
92 , _step_edit_cursor_width (1.0)
93 , _step_edit_cursor_position (0.0)
97 , _optimization_iterator (_events.end())
99 , no_sound_notes (false)
101 _note_group->raise_to_top();
102 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
105 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
106 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
107 TimeAxisViewItem::Visibility visibility)
108 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
110 , _last_channel_selection(0xFFFF)
111 , _model_name(string())
112 , _custom_device_mode(string())
114 , _note_group(new ArdourCanvas::Group(*parent))
120 , _sort_needed (true)
121 , _optimization_iterator (_events.end())
123 , no_sound_notes (false)
125 _note_group->raise_to_top();
126 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
129 MidiRegionView::MidiRegionView (const MidiRegionView& other)
130 : sigc::trackable(other)
133 , _last_channel_selection(0xFFFF)
134 , _model_name(string())
135 , _custom_device_mode(string())
137 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
143 , _sort_needed (true)
144 , _optimization_iterator (_events.end())
146 , no_sound_notes (false)
151 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
152 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
157 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
158 : RegionView (other, boost::shared_ptr<Region> (region))
160 , _last_channel_selection(0xFFFF)
161 , _model_name(string())
162 , _custom_device_mode(string())
164 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
170 , _sort_needed (true)
171 , _optimization_iterator (_events.end())
173 , no_sound_notes (false)
178 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
179 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
185 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
187 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
189 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
190 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
194 midi_region()->midi_source(0)->load_model();
197 _model = midi_region()->midi_source(0)->model();
198 _enable_display = false;
200 RegionView::init (basic_color, false);
202 compute_colors (basic_color);
204 set_height (trackview.current_height());
207 region_sync_changed ();
208 region_resized (ARDOUR::bounds_change);
211 reset_width_dependent_items (_pixel_width);
215 _enable_display = true;
218 display_model (_model);
222 group->raise_to_top();
223 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
225 midi_view()->signal_channel_mode_changed().connect(
226 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
228 midi_view()->signal_midi_patch_settings_changed().connect(
229 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
231 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
235 MidiRegionView::canvas_event(GdkEvent* ev)
237 if (!trackview.editor().internal_editing()) {
241 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
242 to its items, which means that ev->type == GDK_SCROLL will never be seen
247 return scroll (&ev->scroll);
250 return key_press (&ev->key);
252 case GDK_KEY_RELEASE:
253 return key_release (&ev->key);
255 case GDK_BUTTON_PRESS:
256 return button_press (&ev->button);
258 case GDK_2BUTTON_PRESS:
261 case GDK_BUTTON_RELEASE:
262 return button_release (&ev->button);
264 case GDK_ENTER_NOTIFY:
265 return enter_notify (&ev->crossing);
267 case GDK_LEAVE_NOTIFY:
268 return leave_notify (&ev->crossing);
270 case GDK_MOTION_NOTIFY:
271 return motion (&ev->motion);
281 MidiRegionView::enter_notify (GdkEventCrossing* ev)
283 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
285 Keyboard::magic_widget_grab_focus();
288 if (trackview.editor().current_mouse_mode() == MouseRange) {
289 create_ghost_note (ev->x, ev->y);
296 MidiRegionView::leave_notify (GdkEventCrossing* ev)
298 trackview.editor().hide_verbose_canvas_cursor ();
305 MidiRegionView::button_press (GdkEventButton* ev)
309 group->w2i (_last_x, _last_y);
311 if (_mouse_state != SelectTouchDragging && ev->button == 1) {
312 _pressed_button = ev->button;
313 _mouse_state = Pressed;
317 _pressed_button = ev->button;
323 MidiRegionView::button_release (GdkEventButton* ev)
325 double event_x, event_y;
326 nframes64_t event_frame = 0;
330 group->w2i(event_x, event_y);
331 group->ungrab(ev->time);
332 event_frame = trackview.editor().pixel_to_frame(event_x);
334 if (ev->button == 3) {
336 } else if (_pressed_button != 1) {
340 switch (_mouse_state) {
341 case Pressed: // Clicked
342 switch (trackview.editor().current_mouse_mode()) {
346 maybe_select_by_position (ev, event_x, event_y);
352 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
356 create_note_at (event_x, event_y, beats, true);
364 case SelectRectDragging: // Select drag done
370 case AddDragging: // Add drag done
372 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
373 const double x = _drag_rect->property_x1();
374 const double length = trackview.editor().pixel_to_frame
375 (_drag_rect->property_x2() - _drag_rect->property_x1());
377 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), false);
383 create_ghost_note (ev->x, ev->y);
393 MidiRegionView::motion (GdkEventMotion* ev)
395 double event_x, event_y;
396 nframes64_t event_frame = 0;
400 group->w2i(event_x, event_y);
402 // convert event_x to global frame
403 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
404 trackview.editor().snap_to(event_frame);
405 // convert event_frame back to local coordinates relative to position
406 event_frame -= _region->position();
409 update_ghost_note (ev->x, ev->y);
412 /* any motion immediately hides velocity text that may have been visible */
414 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
415 (*i)->hide_velocity ();
418 switch (_mouse_state) {
419 case Pressed: // Maybe start a drag, if we've moved a bit
421 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
422 /* no appreciable movement since the button was pressed */
427 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
428 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
429 Gdk::Cursor(Gdk::FLEUR), ev->time);
432 _drag_start_x = event_x;
433 _drag_start_y = event_y;
435 _drag_rect = new ArdourCanvas::SimpleRect(*group);
436 _drag_rect->property_x1() = event_x;
437 _drag_rect->property_y1() = event_y;
438 _drag_rect->property_x2() = event_x;
439 _drag_rect->property_y2() = event_y;
440 _drag_rect->property_outline_what() = 0xFF;
441 _drag_rect->property_outline_color_rgba()
442 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
443 _drag_rect->property_fill_color_rgba()
444 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
446 _mouse_state = SelectRectDragging;
449 // Add note drag start
450 } else if (trackview.editor().internal_editing()) {
455 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
456 Gdk::Cursor(Gdk::FLEUR), ev->time);
459 _drag_start_x = event_x;
460 _drag_start_y = event_y;
462 _drag_rect = new ArdourCanvas::SimpleRect(*group);
463 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
465 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
466 midi_stream_view()->y_to_note(event_y));
467 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
468 _drag_rect->property_y2() = _drag_rect->property_y1()
469 + floor(midi_stream_view()->note_height());
470 _drag_rect->property_outline_what() = 0xFF;
471 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
472 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
474 _mouse_state = AddDragging;
480 case SelectRectDragging: // Select drag motion
481 case AddDragging: // Add note drag motion
485 GdkModifierType state;
486 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
491 if (_mouse_state == AddDragging)
492 event_x = trackview.editor().frame_to_pixel(event_frame);
495 if (event_x > _drag_start_x)
496 _drag_rect->property_x2() = event_x;
498 _drag_rect->property_x1() = event_x;
501 if (_drag_rect && _mouse_state == SelectRectDragging) {
502 if (event_y > _drag_start_y)
503 _drag_rect->property_y2() = event_y;
505 _drag_rect->property_y1() = event_y;
507 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
513 case SelectTouchDragging:
525 MidiRegionView::scroll (GdkEventScroll* ev)
527 if (_selection.empty()) {
531 trackview.editor().hide_verbose_canvas_cursor ();
533 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
535 if (ev->direction == GDK_SCROLL_UP) {
536 change_velocities (true, fine, false);
537 } else if (ev->direction == GDK_SCROLL_DOWN) {
538 change_velocities (false, fine, false);
544 MidiRegionView::key_press (GdkEventKey* ev)
546 /* since GTK bindings are generally activated on press, and since
547 detectable auto-repeat is the name of the game and only sends
548 repeated presses, carry out key actions at key press, not release.
551 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
552 _mouse_state = SelectTouchDragging;
555 } else if (ev->keyval == GDK_Escape) {
559 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
561 bool start = (ev->keyval == GDK_comma);
562 bool end = (ev->keyval == GDK_period);
563 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
564 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
566 change_note_lengths (fine, shorter, 0.0, start, end);
570 } else if (ev->keyval == GDK_Delete) {
575 } else if (ev->keyval == GDK_Tab) {
577 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
578 goto_previous_note ();
584 } else if (ev->keyval == GDK_Up) {
586 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
587 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
589 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
590 change_velocities (true, fine, allow_smush);
592 transpose (true, fine, allow_smush);
596 } else if (ev->keyval == GDK_Down) {
598 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
599 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
601 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
602 change_velocities (false, fine, allow_smush);
604 transpose (false, fine, allow_smush);
608 } else if (ev->keyval == GDK_Left) {
613 } else if (ev->keyval == GDK_Right) {
618 } else if (ev->keyval == GDK_Control_L) {
627 MidiRegionView::key_release (GdkEventKey* ev)
629 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
637 MidiRegionView::show_list_editor ()
640 _list_editor = new MidiListEditor (trackview.session(), midi_region());
642 _list_editor->present ();
645 /** Add a note to the model, and the view, at a canvas (click) coordinate.
646 * \param x horizontal position in pixels
647 * \param y vertical position in pixels
648 * \param length duration of the note in beats, which will be snapped to the grid
649 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
652 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
654 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
655 MidiStreamView* const view = mtv->midi_view();
657 double note = midi_stream_view()->y_to_note(y);
660 assert(note <= 127.0);
662 // Start of note in frames relative to region start
663 nframes64_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
664 assert(start_frames >= 0);
667 length = frames_to_beats(
668 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
670 assert (length != 0);
673 length = frames_to_beats (beats_to_frames (length) - 1);
676 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
680 /* pick the highest selected channel, unless all channels are selected,
681 which is interpreted to mean channel 1 (zero)
684 for (uint16_t i = 0; i < 16; ++i) {
685 if (chn_mask & (1<<i)) {
695 const boost::shared_ptr<NoteType> new_note (new NoteType (channel,
696 frames_to_beats(start_frames + _region->start()), length,
697 (uint8_t)note, 0x40));
699 if (_model->contains (new_note)) {
703 view->update_note_range(new_note->note());
705 MidiModel::DiffCommand* cmd = _model->new_diff_command("add note");
707 _model->apply_command(*trackview.session(), cmd);
709 play_midi_note (new_note);
713 MidiRegionView::clear_events()
718 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
719 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
724 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
729 _pgm_changes.clear();
731 _optimization_iterator = _events.end();
736 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
739 content_connection.disconnect ();
740 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
744 if (_enable_display) {
750 MidiRegionView::start_diff_command(string name)
752 if (!_diff_command) {
753 _diff_command = _model->new_diff_command(name);
758 MidiRegionView::diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
761 _diff_command->add(note);
764 _marked_for_selection.insert(note);
767 _marked_for_velocity.insert(note);
772 MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
774 if (_diff_command && ev->note()) {
775 _diff_command->remove(ev->note());
780 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
781 MidiModel::DiffCommand::Property property,
785 _diff_command->change (ev->note(), property, val);
790 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
791 MidiModel::DiffCommand::Property property,
792 Evoral::MusicalTime val)
795 _diff_command->change (ev->note(), property, val);
800 MidiRegionView::apply_diff ()
804 if (!_diff_command) {
808 if ((add_or_remove = _diff_command->adds_or_removes())) {
809 // Mark all selected notes for selection when model reloads
810 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
811 _marked_for_selection.insert((*i)->note());
815 _model->apply_command(*trackview.session(), _diff_command);
817 midi_view()->midi_track()->playlist_modified();
820 _marked_for_selection.clear();
823 _marked_for_velocity.clear();
827 MidiRegionView::apply_diff_as_subcommand()
831 if (!_diff_command) {
835 if ((add_or_remove = _diff_command->adds_or_removes())) {
836 // Mark all selected notes for selection when model reloads
837 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
838 _marked_for_selection.insert((*i)->note());
842 _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
844 midi_view()->midi_track()->playlist_modified();
847 _marked_for_selection.clear();
849 _marked_for_velocity.clear();
854 MidiRegionView::abort_command()
856 delete _diff_command;
862 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
864 if (_optimization_iterator != _events.end()) {
865 ++_optimization_iterator;
868 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
869 return *_optimization_iterator;
872 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
873 if ((*_optimization_iterator)->note() == note) {
874 return *_optimization_iterator;
882 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
884 MidiModel::Notes notes;
885 _model->get_notes (notes, op, val, chan_mask);
887 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
888 CanvasNoteEvent* cne = find_canvas_note (*n);
896 MidiRegionView::redisplay_model()
898 // Don't redisplay the model if we're currently recording and displaying that
904 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
908 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
912 MidiModel::ReadLock lock(_model->read_lock());
914 MidiModel::Notes& notes (_model->notes());
915 _optimization_iterator = _events.begin();
917 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
919 boost::shared_ptr<NoteType> note (*n);
920 CanvasNoteEvent* cne;
923 if (note_in_region_range (note, visible)) {
925 if ((cne = find_canvas_note (note)) != 0) {
932 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
934 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
946 add_note (note, visible);
951 if ((cne = find_canvas_note (note)) != 0) {
959 /* remove note items that are no longer valid */
961 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
962 if (!(*i)->valid ()) {
964 i = _events.erase (i);
970 _pgm_changes.clear();
974 display_program_changes();
976 _marked_for_selection.clear ();
977 _marked_for_velocity.clear ();
979 /* we may have caused _events to contain things out of order (e.g. if a note
980 moved earlier or later). we don't generally need them in time order, but
981 make a note that a sort is required for those cases that require it.
988 MidiRegionView::display_program_changes()
990 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
991 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
993 for (uint8_t i = 0; i < 16; ++i) {
994 if (chn_mask & (1<<i)) {
995 display_program_changes_on_channel (i);
1001 MidiRegionView::display_program_changes_on_channel(uint8_t channel)
1003 boost::shared_ptr<Evoral::Control> control =
1004 _model->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation, channel));
1010 Glib::Mutex::Lock lock (control->list()->lock());
1012 for (AutomationList::const_iterator event = control->list()->begin();
1013 event != control->list()->end(); ++event) {
1014 double event_time = (*event)->when;
1015 double program_number = floor((*event)->value + 0.5);
1017 // Get current value of bank select MSB at time of the program change
1018 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1019 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1021 if (msb_control != 0) {
1022 msb = uint8_t(floor(msb_control->get_double(true, event_time) + 0.5));
1025 // Get current value of bank select LSB at time of the program change
1026 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1027 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1029 if (lsb_control != 0) {
1030 lsb = uint8_t(floor(lsb_control->get_double(true, event_time) + 0.5));
1033 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
1035 boost::shared_ptr<MIDI::Name::Patch> patch =
1036 MIDI::Name::MidiPatchManager::instance().find_patch(
1037 _model_name, _custom_device_mode, channel, patch_key);
1039 PCEvent program_change(event_time, uint8_t(program_number), channel);
1042 add_pgm_change(program_change, patch->name());
1045 // program_number is zero-based: convert to one-based
1046 snprintf(buf, 4, "%d", int(program_number+1));
1047 add_pgm_change(program_change, buf);
1053 MidiRegionView::display_sysexes()
1055 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1056 Evoral::MusicalTime time = (*i)->time();
1061 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1062 str << int((*i)->buffer()[b]);
1063 if (b != (*i)->size() -1) {
1067 string text = str.str();
1069 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1071 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1073 double height = midi_stream_view()->contents_height();
1075 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1076 new CanvasSysEx(*this, *group, text, height, x, 1.0));
1078 // Show unless program change is beyond the region bounds
1079 if (time - _region->start() >= _region->length() || time < _region->start()) {
1085 _sys_exes.push_back(sysex);
1090 MidiRegionView::~MidiRegionView ()
1092 in_destructor = true;
1094 trackview.editor().hide_verbose_canvas_cursor ();
1096 note_delete_connection.disconnect ();
1098 delete _list_editor;
1100 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1102 if (_active_notes) {
1110 delete _diff_command;
1111 delete _step_edit_cursor;
1115 MidiRegionView::region_resized (const PropertyChange& what_changed)
1117 RegionView::region_resized(what_changed);
1119 if (what_changed.contains (ARDOUR::Properties::position)) {
1120 set_duration(_region->length(), 0);
1121 if (_enable_display) {
1128 MidiRegionView::reset_width_dependent_items (double pixel_width)
1130 RegionView::reset_width_dependent_items(pixel_width);
1131 assert(_pixel_width == pixel_width);
1133 if (_enable_display) {
1137 move_step_edit_cursor (_step_edit_cursor_position);
1138 set_step_edit_cursor_width (_step_edit_cursor_width);
1142 MidiRegionView::set_height (double height)
1144 static const double FUDGE = 2.0;
1145 const double old_height = _height;
1146 RegionView::set_height(height);
1147 _height = height - FUDGE;
1149 apply_note_range(midi_stream_view()->lowest_note(),
1150 midi_stream_view()->highest_note(),
1151 height != old_height + FUDGE);
1154 name_pixbuf->raise_to_top();
1157 for (PgmChanges::iterator x = _pgm_changes.begin(); x != _pgm_changes.end(); ++x) {
1158 (*x)->set_height (midi_stream_view()->contents_height());
1161 if (_step_edit_cursor) {
1162 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1167 /** Apply the current note range from the stream view
1168 * by repositioning/hiding notes as necessary
1171 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1173 if (!_enable_display) {
1177 if (!force && _current_range_min == min && _current_range_max == max) {
1181 _current_range_min = min;
1182 _current_range_max = max;
1184 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1185 CanvasNoteEvent* event = *i;
1186 boost::shared_ptr<NoteType> note (event->note());
1188 if (note->note() < _current_range_min ||
1189 note->note() > _current_range_max) {
1195 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1197 const double y1 = midi_stream_view()->note_to_y(note->note());
1198 const double y2 = y1 + floor(midi_stream_view()->note_height());
1200 cnote->property_y1() = y1;
1201 cnote->property_y2() = y2;
1203 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1205 double x = trackview.editor().frame_to_pixel(
1206 beats_to_frames(note->time()) - _region->start());
1207 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1208 double y = midi_stream_view()->note_to_y(event->note()->note())
1209 + ((diamond_size-2.0) / 4.0);
1211 chit->set_height (diamond_size);
1212 chit->move (x - chit->x1(), y - chit->y1());
1219 MidiRegionView::add_ghost (TimeAxisView& tv)
1223 double unit_position = _region->position () / samples_per_unit;
1224 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1225 MidiGhostRegion* ghost;
1227 if (mtv && mtv->midi_view()) {
1228 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1229 to allow having midi notes on top of note lines and waveforms.
1231 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1233 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1236 ghost->set_height ();
1237 ghost->set_duration (_region->length() / samples_per_unit);
1238 ghosts.push_back (ghost);
1240 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1241 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1242 ghost->add_note(note);
1246 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1252 /** Begin tracking note state for successive calls to add_event
1255 MidiRegionView::begin_write()
1257 assert(!_active_notes);
1258 _active_notes = new CanvasNote*[128];
1259 for (unsigned i=0; i < 128; ++i) {
1260 _active_notes[i] = 0;
1265 /** Destroy note state for add_event
1268 MidiRegionView::end_write()
1270 delete[] _active_notes;
1272 _marked_for_selection.clear();
1273 _marked_for_velocity.clear();
1277 /** Resolve an active MIDI note (while recording).
1280 MidiRegionView::resolve_note(uint8_t note, double end_time)
1282 if (midi_view()->note_mode() != Sustained) {
1286 if (_active_notes && _active_notes[note]) {
1287 const nframes64_t end_time_frames = beats_to_frames(end_time);
1288 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1289 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1290 _active_notes[note] = 0;
1295 /** Extend active notes to rightmost edge of region (if length is changed)
1298 MidiRegionView::extend_active_notes()
1300 if (!_active_notes) {
1304 for (unsigned i=0; i < 128; ++i) {
1305 if (_active_notes[i]) {
1306 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1312 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1314 if (no_sound_notes || !trackview.editor().sound_notes()) {
1318 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1320 if (!route_ui || !route_ui->midi_track()) {
1324 route_ui->midi_track()->write_immediate_event(
1325 note->on_event().size(), note->on_event().buffer());
1327 const double note_length_beats = (note->off_event().time() - note->on_event().time());
1328 nframes_t note_length_ms = beats_to_frames(note_length_beats)
1329 * (1000 / (double)route_ui->session()->nominal_frame_rate());
1331 /* note: we probably should not be binding a shared_ptr<NoteType>
1332 here. Since its a one-shot timeout, its sort of OK, but ...
1335 Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(this, &MidiRegionView::play_midi_note_off), note),
1336 note_length_ms, G_PRIORITY_DEFAULT);
1340 MidiRegionView::play_midi_note_off(boost::shared_ptr<NoteType> note)
1342 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1344 if (!route_ui || !route_ui->midi_track()) {
1348 route_ui->midi_track()->write_immediate_event(
1349 note->off_event().size(), note->off_event().buffer());
1355 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1357 const nframes64_t note_start_frames = beats_to_frames(note->time());
1359 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1360 (note_start_frames < _region->start());
1362 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1363 (note->note() <= midi_stream_view()->highest_note());
1369 MidiRegionView::update_note (CanvasNote* ev)
1371 boost::shared_ptr<NoteType> note = ev->note();
1373 const nframes64_t note_start_frames = beats_to_frames(note->time());
1375 /* trim note display to not overlap the end of its region */
1376 const nframes64_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1378 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1379 const double y1 = midi_stream_view()->note_to_y(note->note());
1380 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1382 ev->property_x1() = x;
1383 ev->property_y1() = y1;
1384 if (note->length() > 0) {
1385 ev->property_x2() = note_endpixel;
1387 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1389 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1391 if (note->length() == 0) {
1392 if (_active_notes) {
1393 assert(note->note() < 128);
1394 // If this note is already active there's a stuck note,
1395 // finish the old note rectangle
1396 if (_active_notes[note->note()]) {
1397 CanvasNote* const old_rect = _active_notes[note->note()];
1398 boost::shared_ptr<NoteType> old_note = old_rect->note();
1399 old_rect->property_x2() = x;
1400 old_rect->property_outline_what() = (guint32) 0xF;
1402 _active_notes[note->note()] = ev;
1404 /* outline all but right edge */
1405 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1407 /* outline all edges */
1408 ev->property_outline_what() = (guint32) 0xF;
1413 MidiRegionView::update_hit (CanvasHit* ev)
1415 boost::shared_ptr<NoteType> note = ev->note();
1417 const nframes64_t note_start_frames = beats_to_frames(note->time());
1418 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1419 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1420 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1425 /** Add a MIDI note to the view (with length).
1427 * If in sustained mode, notes with length 0 will be considered active
1428 * notes, and resolve_note should be called when the corresponding note off
1429 * event arrives, to properly display the note.
1432 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1434 CanvasNoteEvent* event = 0;
1436 assert(note->time() >= 0);
1437 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1439 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1441 if (midi_view()->note_mode() == Sustained) {
1443 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1445 update_note (ev_rect);
1449 MidiGhostRegion* gr;
1451 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1452 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1453 gr->add_note(ev_rect);
1457 } else if (midi_view()->note_mode() == Percussive) {
1459 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1461 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1463 update_hit (ev_diamond);
1472 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1473 note_selected(event, true);
1476 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1477 event->show_velocity();
1479 event->on_channel_selection_change(_last_channel_selection);
1480 _events.push_back(event);
1491 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1492 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1494 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1496 /* potentially extend region to hold new note */
1498 nframes64_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1499 nframes64_t region_end = _region->position() + _region->length() - 1;
1501 if (end_frame > region_end) {
1502 _region->set_length (end_frame - _region->position(), this);
1505 _marked_for_selection.clear ();
1508 start_diff_command (_("step add"));
1509 diff_add_note (new_note, true, false);
1512 // last_step_edit_note = new_note;
1516 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1518 change_note_lengths (false, false, beats, false, true);
1522 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1524 assert(program.time >= 0);
1526 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1527 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1529 double height = midi_stream_view()->contents_height();
1531 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1532 new CanvasProgramChange(*this, *group,
1537 _custom_device_mode,
1538 program.time, program.channel, program.value));
1540 // Show unless program change is beyond the region bounds
1541 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1547 _pgm_changes.push_back(pgm_change);
1551 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1553 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1554 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1556 if (msb_control != 0) {
1557 msb = int(msb_control->get_double(true, time));
1560 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1561 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1563 if (lsb_control != 0) {
1564 lsb = lsb_control->get_double(true, time);
1567 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1568 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1569 double program_number = -1.0;
1570 if (program_control != 0) {
1571 program_number = program_control->get_double(true, time);
1574 key.msb = (int) floor(msb + 0.5);
1575 key.lsb = (int) floor(lsb + 0.5);
1576 key.program_number = (int) floor(program_number + 0.5);
1577 assert(key.is_sane());
1582 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1584 // TODO: Get the real event here and alter them at the original times
1585 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1586 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1587 if (msb_control != 0) {
1588 msb_control->set_double(double(new_patch.msb), true, old_program.time);
1591 // TODO: Get the real event here and alter them at the original times
1592 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1593 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1594 if (lsb_control != 0) {
1595 lsb_control->set_double(double(new_patch.lsb), true, old_program.time);
1598 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1599 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1601 assert(program_control != 0);
1602 program_control->set_double(float(new_patch.program_number), true, old_program.time);
1604 _pgm_changes.clear ();
1605 display_program_changes (); // XXX would be nice to limit to just old_program.channel
1609 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1611 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1612 alter_program_change(program_change_event, new_patch);
1616 MidiRegionView::previous_program(CanvasProgramChange& program)
1618 if (program.program() < 127) {
1619 MIDI::Name::PatchPrimaryKey key;
1620 get_patch_key_at(program.event_time(), program.channel(), key);
1621 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1623 key.program_number++;
1624 alter_program_change(program_change_event, key);
1629 MidiRegionView::next_program(CanvasProgramChange& program)
1631 if (program.program() > 0) {
1632 MIDI::Name::PatchPrimaryKey key;
1633 get_patch_key_at(program.event_time(), program.channel(), key);
1634 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1636 key.program_number--;
1637 alter_program_change(program_change_event, key);
1642 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1644 if (_selection.empty()) {
1648 if (_selection.erase (cne) > 0) {
1649 cerr << "Erased a CNE from selection\n";
1654 MidiRegionView::delete_selection()
1656 if (_selection.empty()) {
1660 start_diff_command (_("delete selection"));
1662 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1663 if ((*i)->selected()) {
1664 _diff_command->remove((*i)->note());
1674 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1676 start_diff_command (_("delete note"));
1677 _diff_command->remove (n);
1680 trackview.editor().hide_verbose_canvas_cursor ();
1684 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1686 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1687 if ((*i)->selected() && (*i) != ev) {
1688 (*i)->set_selected(false);
1689 (*i)->hide_velocity();
1697 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1699 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1702 Selection::iterator tmp = i;
1705 (*i)->set_selected (false);
1706 _selection.erase (i);
1715 /* don't bother with removing this regionview from the editor selection,
1716 since we're about to add another note, and thus put/keep this
1717 regionview in the editor selection.
1720 if (!ev->selected()) {
1721 add_to_selection (ev);
1726 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1728 uint8_t low_note = 127;
1729 uint8_t high_note = 0;
1730 MidiModel::Notes& notes (_model->notes());
1731 _optimization_iterator = _events.begin();
1737 if (extend && _selection.empty()) {
1743 /* scan existing selection to get note range */
1745 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1746 if ((*i)->note()->note() < low_note) {
1747 low_note = (*i)->note()->note();
1749 if ((*i)->note()->note() > high_note) {
1750 high_note = (*i)->note()->note();
1754 low_note = min (low_note, notenum);
1755 high_note = max (high_note, notenum);
1758 no_sound_notes = true;
1760 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1762 boost::shared_ptr<NoteType> note (*n);
1763 CanvasNoteEvent* cne;
1764 bool select = false;
1766 if (((1 << note->channel()) & channel_mask) != 0) {
1768 if ((note->note() >= low_note && note->note() <= high_note)) {
1771 } else if (note->note() == notenum) {
1777 if ((cne = find_canvas_note (note)) != 0) {
1778 // extend is false because we've taken care of it,
1779 // since it extends by time range, not pitch.
1780 note_selected (cne, add, false);
1784 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1788 no_sound_notes = false;
1792 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1794 MidiModel::Notes& notes (_model->notes());
1795 _optimization_iterator = _events.begin();
1797 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1799 boost::shared_ptr<NoteType> note (*n);
1800 CanvasNoteEvent* cne;
1802 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1803 if ((cne = find_canvas_note (note)) != 0) {
1804 if (cne->selected()) {
1805 note_deselected (cne);
1807 note_selected (cne, true, false);
1815 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1818 clear_selection_except(ev);
1823 if (!ev->selected()) {
1824 add_to_selection (ev);
1828 /* find end of latest note selected, select all between that and the start of "ev" */
1830 Evoral::MusicalTime earliest = DBL_MAX;
1831 Evoral::MusicalTime latest = 0;
1833 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1834 if ((*i)->note()->end_time() > latest) {
1835 latest = (*i)->note()->end_time();
1837 if ((*i)->note()->time() < earliest) {
1838 earliest = (*i)->note()->time();
1842 if (ev->note()->end_time() > latest) {
1843 latest = ev->note()->end_time();
1846 if (ev->note()->time() < earliest) {
1847 earliest = ev->note()->time();
1850 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1852 /* find notes entirely within OR spanning the earliest..latest range */
1854 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1855 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1856 add_to_selection (*i);
1860 /* if events were guaranteed to be time sorted, we could do this.
1861 but as of sept 10th 2009, they no longer are.
1864 if ((*i)->note()->time() > latest) {
1873 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1875 remove_from_selection (ev);
1879 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1889 // TODO: Make this faster by storing the last updated selection rect, and only
1890 // adjusting things that are in the area that appears/disappeared.
1891 // We probably need a tree to be able to find events in O(log(n)) time.
1893 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1895 /* check if any corner of the note is inside the rect
1898 1) this is computing "touched by", not "contained by" the rect.
1899 2) this does not require that events be sorted in time.
1902 const double ix1 = (*i)->x1();
1903 const double ix2 = (*i)->x2();
1904 const double iy1 = (*i)->y1();
1905 const double iy2 = (*i)->y2();
1907 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1908 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1909 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1910 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1913 if (!(*i)->selected()) {
1914 add_to_selection (*i);
1916 } else if ((*i)->selected()) {
1917 // Not inside rectangle
1918 remove_from_selection (*i);
1924 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1926 Selection::iterator i = _selection.find (ev);
1928 if (i != _selection.end()) {
1929 _selection.erase (i);
1932 ev->set_selected (false);
1933 ev->hide_velocity ();
1935 if (_selection.empty()) {
1936 PublicEditor& editor (trackview.editor());
1937 editor.get_selection().remove (this);
1942 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
1944 bool add_mrv_selection = false;
1946 if (_selection.empty()) {
1947 add_mrv_selection = true;
1950 if (_selection.insert (ev).second) {
1951 ev->set_selected (true);
1952 play_midi_note ((ev)->note());
1955 if (add_mrv_selection) {
1956 PublicEditor& editor (trackview.editor());
1957 editor.get_selection().add (this);
1962 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
1964 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1965 (*i)->move_event(dx, dy);
1968 boost::shared_ptr<NoteType>
1969 moved_note (new NoteType (*((*i)->note())));
1970 moved_note->set_note (moved_note->note() + cumulative_dy);
1971 play_midi_note (moved_note);
1977 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
1979 assert (!_selection.empty());
1981 uint8_t lowest_note_in_selection = 127;
1982 uint8_t highest_note_in_selection = 0;
1983 uint8_t highest_note_difference = 0;
1985 // find highest and lowest notes first
1987 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1988 uint8_t pitch = (*i)->note()->note();
1989 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
1990 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
1994 cerr << "dnote: " << (int) dnote << endl;
1995 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
1996 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
1997 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
1998 << int(highest_note_in_selection) << endl;
1999 cerr << "selection size: " << _selection.size() << endl;
2000 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2003 // Make sure the note pitch does not exceed the MIDI standard range
2004 if (highest_note_in_selection + dnote > 127) {
2005 highest_note_difference = highest_note_in_selection - 127;
2008 start_diff_command(_("move notes"));
2010 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2012 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2018 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
2020 uint8_t original_pitch = (*i)->note()->note();
2021 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2023 // keep notes in standard midi range
2024 clamp_to_0_127(new_pitch);
2026 // keep original pitch if note is dragged outside valid midi range
2027 if ((original_pitch != 0 && new_pitch == 0)
2028 || (original_pitch != 127 && new_pitch == 127)) {
2029 new_pitch = original_pitch;
2032 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2033 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2035 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
2040 // care about notes being moved beyond the upper/lower bounds on the canvas
2041 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2042 highest_note_in_selection > midi_stream_view()->highest_note()) {
2043 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2048 MidiRegionView::snap_pixel_to_frame(double x)
2050 PublicEditor& editor = trackview.editor();
2051 // x is region relative, convert it to global absolute frames
2052 nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
2053 editor.snap_to(frame);
2054 return frame - _region->position(); // convert back to region relative
2058 MidiRegionView::snap_frame_to_frame(nframes64_t x)
2060 PublicEditor& editor = trackview.editor();
2061 // x is region relative, convert it to global absolute frames
2062 nframes64_t frame = x + _region->position();
2063 editor.snap_to(frame);
2064 return frame - _region->position(); // convert back to region relative
2068 MidiRegionView::snap_to_pixel(double x)
2070 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2074 MidiRegionView::get_position_pixels()
2076 nframes64_t region_frame = get_position();
2077 return trackview.editor().frame_to_pixel(region_frame);
2081 MidiRegionView::get_end_position_pixels()
2083 nframes64_t frame = get_position() + get_duration ();
2084 return trackview.editor().frame_to_pixel(frame);
2088 MidiRegionView::beats_to_frames(double beats) const
2090 return _time_converter.to(beats);
2094 MidiRegionView::frames_to_beats(nframes64_t frames) const
2096 return _time_converter.from(frames);
2100 MidiRegionView::begin_resizing (bool /*at_front*/)
2102 _resize_data.clear();
2104 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2105 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2107 // only insert CanvasNotes into the map
2109 NoteResizeData *resize_data = new NoteResizeData();
2110 resize_data->canvas_note = note;
2112 // create a new SimpleRect from the note which will be the resize preview
2113 SimpleRect *resize_rect = new SimpleRect(
2114 *group, note->x1(), note->y1(), note->x2(), note->y2());
2116 // calculate the colors: get the color settings
2117 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2118 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2121 // make the resize preview notes more transparent and bright
2122 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2124 // calculate color based on note velocity
2125 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2126 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2130 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2131 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2133 resize_data->resize_rect = resize_rect;
2134 _resize_data.push_back(resize_data);
2139 /** Update resizing notes while user drags.
2140 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2141 * @param at_front which end of the note (true == note on, false == note off)
2142 * @param delta_x change in mouse position since the start of the drag
2143 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2144 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2145 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2146 * as the \a primary note.
2149 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2151 bool cursor_set = false;
2153 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2154 SimpleRect* resize_rect = (*i)->resize_rect;
2155 CanvasNote* canvas_note = (*i)->canvas_note;
2160 current_x = canvas_note->x1() + delta_x;
2162 current_x = primary->x1() + delta_x;
2166 current_x = canvas_note->x2() + delta_x;
2168 current_x = primary->x2() + delta_x;
2173 resize_rect->property_x1() = snap_to_pixel(current_x);
2174 resize_rect->property_x2() = canvas_note->x2();
2176 resize_rect->property_x2() = snap_to_pixel(current_x);
2177 resize_rect->property_x1() = canvas_note->x1();
2183 beats = snap_pixel_to_frame (current_x);
2184 beats = frames_to_beats (beats);
2189 if (beats < canvas_note->note()->end_time()) {
2190 len = canvas_note->note()->time() - beats;
2191 len += canvas_note->note()->length();
2196 if (beats >= canvas_note->note()->end_time()) {
2197 len = beats - canvas_note->note()->time();
2204 snprintf (buf, sizeof (buf), "%.3g beats", len);
2205 trackview.editor().show_verbose_canvas_cursor_with (buf);
2214 /** Finish resizing notes when the user releases the mouse button.
2215 * Parameters the same as for \a update_resizing().
2218 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2220 start_diff_command(_("resize notes"));
2222 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2223 CanvasNote* canvas_note = (*i)->canvas_note;
2224 SimpleRect* resize_rect = (*i)->resize_rect;
2229 current_x = canvas_note->x1() + delta_x;
2231 current_x = primary->x1() + delta_x;
2235 current_x = canvas_note->x2() + delta_x;
2237 current_x = primary->x2() + delta_x;
2241 current_x = snap_pixel_to_frame (current_x);
2242 current_x = frames_to_beats (current_x);
2244 if (at_front && current_x < canvas_note->note()->end_time()) {
2245 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2247 double len = canvas_note->note()->time() - current_x;
2248 len += canvas_note->note()->length();
2251 /* XXX convert to beats */
2252 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2257 double len = current_x - canvas_note->note()->time();
2260 /* XXX convert to beats */
2261 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2269 _resize_data.clear();
2274 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2276 diff_add_change (event, MidiModel::DiffCommand::Channel, (uint8_t) channel);
2280 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2282 uint8_t new_velocity;
2285 new_velocity = event->note()->velocity() + velocity;
2286 clamp_to_0_127(new_velocity);
2288 new_velocity = velocity;
2291 event->set_selected (event->selected()); // change color
2293 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2297 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2302 new_note = event->note()->note() + note;
2307 clamp_to_0_127 (new_note);
2308 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2312 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2314 bool change_start = false;
2315 bool change_length = false;
2316 Evoral::MusicalTime new_start = 0;
2317 Evoral::MusicalTime new_length = 0;
2319 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2321 front_delta: if positive - move the start of the note later in time (shortening it)
2322 if negative - move the start of the note earlier in time (lengthening it)
2324 end_delta: if positive - move the end of the note later in time (lengthening it)
2325 if negative - move the end of the note earlier in time (shortening it)
2329 if (front_delta < 0) {
2331 if (event->note()->time() < -front_delta) {
2334 new_start = event->note()->time() + front_delta; // moves earlier
2337 /* start moved toward zero, so move the end point out to where it used to be.
2338 Note that front_delta is negative, so this increases the length.
2341 new_length = event->note()->length() - front_delta;
2342 change_start = true;
2343 change_length = true;
2347 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2349 if (new_pos < event->note()->end_time()) {
2350 new_start = event->note()->time() + front_delta;
2351 /* start moved toward the end, so move the end point back to where it used to be */
2352 new_length = event->note()->length() - front_delta;
2353 change_start = true;
2354 change_length = true;
2361 bool can_change = true;
2362 if (end_delta < 0) {
2363 if (event->note()->length() < -end_delta) {
2369 new_length = event->note()->length() + end_delta;
2370 change_length = true;
2375 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2378 if (change_length) {
2379 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2384 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2386 Evoral::MusicalTime new_time;
2390 if (event->note()->time() < -delta) {
2393 new_time = event->note()->time() + delta;
2396 new_time = event->note()->time() + delta;
2402 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2406 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2408 diff_add_change (event, MidiModel::DiffCommand::Length, t);
2412 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2416 if (_selection.empty()) {
2431 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2432 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2438 start_diff_command(_("change velocities"));
2440 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2441 Selection::iterator next = i;
2443 change_note_velocity (*i, delta, true);
2449 if (!_selection.empty()) {
2451 snprintf (buf, sizeof (buf), "Vel %d",
2452 (int) (*_selection.begin())->note()->velocity());
2453 trackview.editor().show_verbose_canvas_cursor_with (buf);
2459 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2461 if (_selection.empty()) {
2478 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2480 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2484 if ((int8_t) (*i)->note()->note() + delta > 127) {
2491 start_diff_command (_("transpose"));
2493 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2494 Selection::iterator next = i;
2496 change_note_note (*i, delta, true);
2504 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2510 /* grab the current grid distance */
2512 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2514 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2515 cerr << "Grid type not available as beats - TO BE FIXED\n";
2525 start_diff_command (_("change note lengths"));
2527 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2528 Selection::iterator next = i;
2531 /* note the negation of the delta for start */
2533 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2542 MidiRegionView::nudge_notes (bool forward)
2544 if (_selection.empty()) {
2548 /* pick a note as the point along the timeline to get the nudge distance.
2549 its not necessarily the earliest note, so we may want to pull the notes out
2550 into a vector and sort before using the first one.
2553 nframes64_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2555 nframes64_t distance;
2557 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2559 /* grid is off - use nudge distance */
2561 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2567 nframes64_t next_pos = ref_point;
2570 /* XXX need check on max_frames, but that needs max_frames64 or something */
2573 if (next_pos == 0) {
2579 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2580 distance = ref_point - next_pos;
2583 if (distance == 0) {
2587 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2593 start_diff_command (_("nudge"));
2595 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2596 Selection::iterator next = i;
2598 change_note_time (*i, delta, true);
2606 MidiRegionView::change_channel(uint8_t channel)
2608 start_diff_command(_("change channel"));
2609 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2610 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2618 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2620 if (_mouse_state == SelectTouchDragging) {
2621 note_selected (ev, true);
2624 show_verbose_canvas_cursor (ev->note ());
2628 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent* note)
2630 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2631 (*i)->hide_velocity ();
2634 trackview.editor().hide_verbose_canvas_cursor ();
2638 MidiRegionView::switch_source(boost::shared_ptr<Source> src)
2640 boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(src);
2642 display_model(msrc->model());
2646 MidiRegionView::set_frame_color()
2649 if (_selected && should_show_selection) {
2650 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2652 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2658 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2662 case FilterChannels:
2663 _force_channel = -1;
2666 _force_channel = mask;
2667 mask = 0xFFFF; // Show all notes as active (below)
2670 // Update notes for selection
2671 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2672 (*i)->on_channel_selection_change(mask);
2675 _last_channel_selection = mask;
2679 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2681 _model_name = model;
2682 _custom_device_mode = custom_device_mode;
2687 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2689 if (_selection.empty()) {
2693 PublicEditor& editor (trackview.editor());
2698 editor.get_cut_buffer().add (selection_as_cut_buffer());
2706 start_diff_command();
2708 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2714 diff_remove_note (*i);
2724 MidiRegionView::selection_as_cut_buffer () const
2728 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2729 NoteType* n = (*i)->note().get();
2730 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2733 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2740 MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
2746 start_diff_command (_("paste"));
2748 Evoral::MusicalTime beat_delta;
2749 Evoral::MusicalTime paste_pos_beats;
2750 Evoral::MusicalTime duration;
2751 Evoral::MusicalTime end_point = 0;
2753 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2754 paste_pos_beats = frames_to_beats (pos - _region->position());
2755 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2756 paste_pos_beats = 0;
2758 _selection.clear ();
2760 for (int n = 0; n < (int) times; ++n) {
2762 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2764 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2765 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2767 /* make all newly added notes selected */
2769 diff_add_note (copied_note, true);
2770 end_point = copied_note->end_time();
2773 paste_pos_beats += duration;
2776 /* if we pasted past the current end of the region, extend the region */
2778 nframes64_t end_frame = _region->position() + beats_to_frames (end_point);
2779 nframes64_t region_end = _region->position() + _region->length() - 1;
2781 if (end_frame > region_end) {
2783 trackview.session()->begin_reversible_command (_("paste"));
2785 _region->clear_changes ();
2786 _region->set_length (end_frame, this);
2787 trackview.session()->add_command (new StatefulDiffCommand (_region));
2793 struct EventNoteTimeEarlyFirstComparator {
2794 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2795 return a->note()->time() < b->note()->time();
2800 MidiRegionView::time_sort_events ()
2802 if (!_sort_needed) {
2806 EventNoteTimeEarlyFirstComparator cmp;
2809 _sort_needed = false;
2813 MidiRegionView::goto_next_note ()
2815 // nframes64_t pos = -1;
2816 bool use_next = false;
2818 if (_events.back()->selected()) {
2822 time_sort_events ();
2824 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2825 if ((*i)->selected()) {
2828 } else if (use_next) {
2830 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2835 /* use the first one */
2837 unique_select (_events.front());
2842 MidiRegionView::goto_previous_note ()
2844 // nframes64_t pos = -1;
2845 bool use_next = false;
2847 if (_events.front()->selected()) {
2851 time_sort_events ();
2853 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2854 if ((*i)->selected()) {
2857 } else if (use_next) {
2859 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2864 /* use the last one */
2866 unique_select (*(_events.rbegin()));
2870 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2872 bool had_selected = false;
2874 time_sort_events ();
2876 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2877 if ((*i)->selected()) {
2878 selected.insert ((*i)->note());
2879 had_selected = true;
2883 if (allow_all_if_none_selected && !had_selected) {
2884 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2885 selected.insert ((*i)->note());
2891 MidiRegionView::update_ghost_note (double x, double y)
2897 nframes64_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
2898 trackview.editor().snap_to (f);
2899 f -= _region->position ();
2902 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
2907 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
2909 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
2910 _ghost_note->note()->set_length (length);
2911 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
2913 update_note (_ghost_note);
2915 show_verbose_canvas_cursor (_ghost_note->note ());
2919 MidiRegionView::create_ghost_note (double x, double y)
2924 boost::shared_ptr<NoteType> g (new NoteType);
2925 _ghost_note = new NoEventCanvasNote (*this, *group, g);
2926 update_ghost_note (x, y);
2927 _ghost_note->show ();
2932 show_verbose_canvas_cursor (_ghost_note->note ());
2936 MidiRegionView::snap_changed ()
2942 create_ghost_note (_last_ghost_x, _last_ghost_y);
2946 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
2949 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
2950 Evoral::midi_note_name (n->note()).c_str(),
2952 (int) n->velocity());
2953 trackview.editor().show_verbose_canvas_cursor_with (buf);
2957 MidiRegionView::drop_down_keys ()
2959 _mouse_state = None;
2963 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double x, double y)
2965 double note = midi_stream_view()->y_to_note(y);
2967 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
2969 cerr << "Selecting by position\n";
2971 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
2973 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
2974 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
2975 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
2976 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
2981 bool add_mrv_selection = false;
2983 if (_selection.empty()) {
2984 add_mrv_selection = true;
2987 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
2988 if (_selection.insert (*i).second) {
2989 (*i)->set_selected (true);
2993 if (add_mrv_selection) {
2994 PublicEditor& editor (trackview.editor());
2995 editor.get_selection().add (this);
3000 MidiRegionView::color_handler ()
3002 RegionView::color_handler ();
3004 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3005 (*i)->set_selected ((*i)->selected()); // will change color
3008 /* XXX probably more to do here */
3012 MidiRegionView::enable_display (bool yn)
3014 RegionView::enable_display (yn);
3021 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3023 if (_step_edit_cursor == 0) {
3024 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3026 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3027 _step_edit_cursor->property_y1() = 0;
3028 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3029 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3030 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3033 move_step_edit_cursor (pos);
3034 _step_edit_cursor->show ();
3038 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3040 _step_edit_cursor_position = pos;
3042 if (_step_edit_cursor) {
3043 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3044 _step_edit_cursor->property_x1() = pixel;
3045 set_step_edit_cursor_width (_step_edit_cursor_width);
3050 MidiRegionView::hide_step_edit_cursor ()
3052 if (_step_edit_cursor) {
3053 _step_edit_cursor->hide ();
3058 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3060 _step_edit_cursor_width = beats;
3062 if (_step_edit_cursor) {
3063 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));