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 "note_player.h"
63 #include "public_editor.h"
64 #include "rgb_macros.h"
65 #include "selection.h"
66 #include "simpleline.h"
67 #include "streamview.h"
72 using namespace ARDOUR;
74 using namespace Editing;
75 using namespace ArdourCanvas;
76 using Gtkmm2ext::Keyboard;
78 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
79 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
80 : RegionView (parent, tv, r, spu, basic_color)
82 , _last_channel_selection(0xFFFF)
83 , _current_range_min(0)
84 , _current_range_max(0)
85 , _model_name(string())
86 , _custom_device_mode(string())
88 , _note_group(new ArdourCanvas::Group(*parent))
92 , _step_edit_cursor (0)
93 , _step_edit_cursor_width (1.0)
94 , _step_edit_cursor_position (0.0)
98 , _optimization_iterator (_events.end())
100 , no_sound_notes (false)
102 _note_group->raise_to_top();
103 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
106 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
107 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
108 TimeAxisViewItem::Visibility visibility)
109 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
111 , _last_channel_selection(0xFFFF)
112 , _model_name(string())
113 , _custom_device_mode(string())
115 , _note_group(new ArdourCanvas::Group(*parent))
121 , _sort_needed (true)
122 , _optimization_iterator (_events.end())
124 , no_sound_notes (false)
126 _note_group->raise_to_top();
127 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
130 MidiRegionView::MidiRegionView (const MidiRegionView& other)
131 : sigc::trackable(other)
134 , _last_channel_selection(0xFFFF)
135 , _model_name(string())
136 , _custom_device_mode(string())
138 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
144 , _sort_needed (true)
145 , _optimization_iterator (_events.end())
147 , no_sound_notes (false)
152 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
153 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
158 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
159 : RegionView (other, boost::shared_ptr<Region> (region))
161 , _last_channel_selection(0xFFFF)
162 , _model_name(string())
163 , _custom_device_mode(string())
165 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
171 , _sort_needed (true)
172 , _optimization_iterator (_events.end())
174 , no_sound_notes (false)
179 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
180 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
186 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
188 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
190 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
191 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
195 midi_region()->midi_source(0)->load_model();
198 _model = midi_region()->midi_source(0)->model();
199 _enable_display = false;
201 RegionView::init (basic_color, false);
203 compute_colors (basic_color);
205 set_height (trackview.current_height());
208 region_sync_changed ();
209 region_resized (ARDOUR::bounds_change);
212 reset_width_dependent_items (_pixel_width);
216 _enable_display = true;
219 display_model (_model);
223 group->raise_to_top();
224 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
226 midi_view()->signal_channel_mode_changed().connect(
227 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
229 midi_view()->signal_midi_patch_settings_changed().connect(
230 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
232 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
236 MidiRegionView::canvas_event(GdkEvent* ev)
238 if (!trackview.editor().internal_editing()) {
242 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
243 to its items, which means that ev->type == GDK_SCROLL will never be seen
248 return scroll (&ev->scroll);
251 return key_press (&ev->key);
253 case GDK_KEY_RELEASE:
254 return key_release (&ev->key);
256 case GDK_BUTTON_PRESS:
257 return button_press (&ev->button);
259 case GDK_2BUTTON_PRESS:
262 case GDK_BUTTON_RELEASE:
263 return button_release (&ev->button);
265 case GDK_ENTER_NOTIFY:
266 return enter_notify (&ev->crossing);
268 case GDK_LEAVE_NOTIFY:
269 return leave_notify (&ev->crossing);
271 case GDK_MOTION_NOTIFY:
272 return motion (&ev->motion);
282 MidiRegionView::enter_notify (GdkEventCrossing* ev)
284 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
286 Keyboard::magic_widget_grab_focus();
289 if (trackview.editor().current_mouse_mode() == MouseRange) {
290 create_ghost_note (ev->x, ev->y);
297 MidiRegionView::leave_notify (GdkEventCrossing* ev)
299 trackview.editor().hide_verbose_canvas_cursor ();
306 MidiRegionView::button_press (GdkEventButton* ev)
310 group->w2i (_last_x, _last_y);
312 if (_mouse_state != SelectTouchDragging && ev->button == 1) {
313 _pressed_button = ev->button;
314 _mouse_state = Pressed;
318 _pressed_button = ev->button;
324 MidiRegionView::button_release (GdkEventButton* ev)
326 double event_x, event_y;
327 framepos_t event_frame = 0;
331 group->w2i(event_x, event_y);
332 group->ungrab(ev->time);
333 event_frame = trackview.editor().pixel_to_frame(event_x);
335 if (ev->button == 3) {
337 } else if (_pressed_button != 1) {
341 switch (_mouse_state) {
342 case Pressed: // Clicked
343 switch (trackview.editor().current_mouse_mode()) {
347 maybe_select_by_position (ev, event_x, event_y);
353 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
357 create_note_at (event_x, event_y, beats, true);
365 case SelectRectDragging: // Select drag done
371 case AddDragging: // Add drag done
373 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
374 const double x = _drag_rect->property_x1();
375 const double length = trackview.editor().pixel_to_frame
376 (_drag_rect->property_x2() - _drag_rect->property_x1());
378 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), false);
384 create_ghost_note (ev->x, ev->y);
394 MidiRegionView::motion (GdkEventMotion* ev)
396 double event_x, event_y;
397 framepos_t event_frame = 0;
401 group->w2i(event_x, event_y);
403 // convert event_x to global frame
404 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
405 trackview.editor().snap_to(event_frame);
406 // convert event_frame back to local coordinates relative to position
407 event_frame -= _region->position();
410 update_ghost_note (ev->x, ev->y);
413 /* any motion immediately hides velocity text that may have been visible */
415 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
416 (*i)->hide_velocity ();
419 switch (_mouse_state) {
420 case Pressed: // Maybe start a drag, if we've moved a bit
422 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
423 /* no appreciable movement since the button was pressed */
428 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
429 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
430 Gdk::Cursor(Gdk::FLEUR), ev->time);
433 _drag_start_x = event_x;
434 _drag_start_y = event_y;
436 _drag_rect = new ArdourCanvas::SimpleRect(*group);
437 _drag_rect->property_x1() = event_x;
438 _drag_rect->property_y1() = event_y;
439 _drag_rect->property_x2() = event_x;
440 _drag_rect->property_y2() = event_y;
441 _drag_rect->property_outline_what() = 0xFF;
442 _drag_rect->property_outline_color_rgba()
443 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
444 _drag_rect->property_fill_color_rgba()
445 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
447 _mouse_state = SelectRectDragging;
450 // Add note drag start
451 } else if (trackview.editor().internal_editing()) {
456 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
457 Gdk::Cursor(Gdk::FLEUR), ev->time);
460 _drag_start_x = event_x;
461 _drag_start_y = event_y;
463 _drag_rect = new ArdourCanvas::SimpleRect(*group);
464 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
466 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
467 midi_stream_view()->y_to_note(event_y));
468 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
469 _drag_rect->property_y2() = _drag_rect->property_y1()
470 + floor(midi_stream_view()->note_height());
471 _drag_rect->property_outline_what() = 0xFF;
472 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
473 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
475 _mouse_state = AddDragging;
481 case SelectRectDragging: // Select drag motion
482 case AddDragging: // Add note drag motion
486 GdkModifierType state;
487 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
492 if (_mouse_state == AddDragging)
493 event_x = trackview.editor().frame_to_pixel(event_frame);
496 if (event_x > _drag_start_x)
497 _drag_rect->property_x2() = event_x;
499 _drag_rect->property_x1() = event_x;
502 if (_drag_rect && _mouse_state == SelectRectDragging) {
503 if (event_y > _drag_start_y)
504 _drag_rect->property_y2() = event_y;
506 _drag_rect->property_y1() = event_y;
508 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
514 case SelectTouchDragging:
526 MidiRegionView::scroll (GdkEventScroll* ev)
528 if (_selection.empty()) {
532 trackview.editor().hide_verbose_canvas_cursor ();
534 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
536 if (ev->direction == GDK_SCROLL_UP) {
537 change_velocities (true, fine, false);
538 } else if (ev->direction == GDK_SCROLL_DOWN) {
539 change_velocities (false, fine, false);
545 MidiRegionView::key_press (GdkEventKey* ev)
547 /* since GTK bindings are generally activated on press, and since
548 detectable auto-repeat is the name of the game and only sends
549 repeated presses, carry out key actions at key press, not release.
552 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
553 _mouse_state = SelectTouchDragging;
556 } else if (ev->keyval == GDK_Escape) {
560 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
562 bool start = (ev->keyval == GDK_comma);
563 bool end = (ev->keyval == GDK_period);
564 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
565 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
567 change_note_lengths (fine, shorter, 0.0, start, end);
571 } else if (ev->keyval == GDK_Delete) {
576 } else if (ev->keyval == GDK_Tab) {
578 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
579 goto_previous_note ();
585 } else if (ev->keyval == GDK_Up) {
587 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
588 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
590 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
591 change_velocities (true, fine, allow_smush);
593 transpose (true, fine, allow_smush);
597 } else if (ev->keyval == GDK_Down) {
599 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
600 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
602 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
603 change_velocities (false, fine, allow_smush);
605 transpose (false, fine, allow_smush);
609 } else if (ev->keyval == GDK_Left) {
614 } else if (ev->keyval == GDK_Right) {
619 } else if (ev->keyval == GDK_Control_L) {
628 MidiRegionView::key_release (GdkEventKey* ev)
630 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
638 MidiRegionView::show_list_editor ()
641 _list_editor = new MidiListEditor (trackview.session(), midi_region());
643 _list_editor->present ();
646 /** Add a note to the model, and the view, at a canvas (click) coordinate.
647 * \param x horizontal position in pixels
648 * \param y vertical position in pixels
649 * \param length duration of the note in beats, which will be snapped to the grid
650 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
653 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
655 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
656 MidiStreamView* const view = mtv->midi_view();
658 double note = midi_stream_view()->y_to_note(y);
661 assert(note <= 127.0);
663 // Start of note in frames relative to region start
664 framepos_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
665 assert(start_frames >= 0);
668 length = frames_to_beats(
669 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
671 assert (length != 0);
674 length = frames_to_beats (beats_to_frames (length) - 1);
677 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
681 /* pick the highest selected channel, unless all channels are selected,
682 which is interpreted to mean channel 1 (zero)
685 for (uint16_t i = 0; i < 16; ++i) {
686 if (chn_mask & (1<<i)) {
696 const boost::shared_ptr<NoteType> new_note (new NoteType (channel,
697 frames_to_beats(start_frames + _region->start()), length,
698 (uint8_t)note, 0x40));
700 if (_model->contains (new_note)) {
704 view->update_note_range(new_note->note());
706 MidiModel::DiffCommand* cmd = _model->new_diff_command("add note");
708 _model->apply_command(*trackview.session(), cmd);
710 play_midi_note (new_note);
714 MidiRegionView::clear_events()
719 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
720 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
725 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
730 _pgm_changes.clear();
732 _optimization_iterator = _events.end();
737 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
740 content_connection.disconnect ();
741 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
745 if (_enable_display) {
751 MidiRegionView::start_diff_command(string name)
753 if (!_diff_command) {
754 _diff_command = _model->new_diff_command(name);
759 MidiRegionView::diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
762 _diff_command->add(note);
765 _marked_for_selection.insert(note);
768 _marked_for_velocity.insert(note);
773 MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
775 if (_diff_command && ev->note()) {
776 _diff_command->remove(ev->note());
781 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
782 MidiModel::DiffCommand::Property property,
786 _diff_command->change (ev->note(), property, val);
791 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
792 MidiModel::DiffCommand::Property property,
793 Evoral::MusicalTime val)
796 _diff_command->change (ev->note(), property, val);
801 MidiRegionView::apply_diff ()
805 if (!_diff_command) {
809 if ((add_or_remove = _diff_command->adds_or_removes())) {
810 // Mark all selected notes for selection when model reloads
811 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
812 _marked_for_selection.insert((*i)->note());
816 _model->apply_command(*trackview.session(), _diff_command);
818 midi_view()->midi_track()->playlist_modified();
821 _marked_for_selection.clear();
824 _marked_for_velocity.clear();
828 MidiRegionView::apply_diff_as_subcommand()
832 if (!_diff_command) {
836 if ((add_or_remove = _diff_command->adds_or_removes())) {
837 // Mark all selected notes for selection when model reloads
838 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
839 _marked_for_selection.insert((*i)->note());
843 _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
845 midi_view()->midi_track()->playlist_modified();
848 _marked_for_selection.clear();
850 _marked_for_velocity.clear();
855 MidiRegionView::abort_command()
857 delete _diff_command;
863 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
865 if (_optimization_iterator != _events.end()) {
866 ++_optimization_iterator;
869 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
870 return *_optimization_iterator;
873 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
874 if ((*_optimization_iterator)->note() == note) {
875 return *_optimization_iterator;
883 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
885 MidiModel::Notes notes;
886 _model->get_notes (notes, op, val, chan_mask);
888 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
889 CanvasNoteEvent* cne = find_canvas_note (*n);
897 MidiRegionView::redisplay_model()
899 // Don't redisplay the model if we're currently recording and displaying that
905 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
909 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
913 MidiModel::ReadLock lock(_model->read_lock());
915 MidiModel::Notes& notes (_model->notes());
916 _optimization_iterator = _events.begin();
918 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
920 boost::shared_ptr<NoteType> note (*n);
921 CanvasNoteEvent* cne;
924 if (note_in_region_range (note, visible)) {
926 if ((cne = find_canvas_note (note)) != 0) {
933 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
935 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
947 add_note (note, visible);
952 if ((cne = find_canvas_note (note)) != 0) {
960 /* remove note items that are no longer valid */
962 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
963 if (!(*i)->valid ()) {
965 i = _events.erase (i);
971 _pgm_changes.clear();
975 display_program_changes();
977 _marked_for_selection.clear ();
978 _marked_for_velocity.clear ();
980 /* we may have caused _events to contain things out of order (e.g. if a note
981 moved earlier or later). we don't generally need them in time order, but
982 make a note that a sort is required for those cases that require it.
989 MidiRegionView::display_program_changes()
991 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
992 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
994 for (uint8_t i = 0; i < 16; ++i) {
995 if (chn_mask & (1<<i)) {
996 display_program_changes_on_channel (i);
1002 MidiRegionView::display_program_changes_on_channel(uint8_t channel)
1004 boost::shared_ptr<Evoral::Control> control =
1005 _model->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation, channel));
1011 Glib::Mutex::Lock lock (control->list()->lock());
1013 for (AutomationList::const_iterator event = control->list()->begin();
1014 event != control->list()->end(); ++event) {
1015 double event_time = (*event)->when;
1016 double program_number = floor((*event)->value + 0.5);
1018 // Get current value of bank select MSB at time of the program change
1019 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1020 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1022 if (msb_control != 0) {
1023 msb = uint8_t(floor(msb_control->get_double(true, event_time) + 0.5));
1026 // Get current value of bank select LSB at time of the program change
1027 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1028 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1030 if (lsb_control != 0) {
1031 lsb = uint8_t(floor(lsb_control->get_double(true, event_time) + 0.5));
1034 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
1036 boost::shared_ptr<MIDI::Name::Patch> patch =
1037 MIDI::Name::MidiPatchManager::instance().find_patch(
1038 _model_name, _custom_device_mode, channel, patch_key);
1040 PCEvent program_change(event_time, uint8_t(program_number), channel);
1043 add_pgm_change(program_change, patch->name());
1046 // program_number is zero-based: convert to one-based
1047 snprintf(buf, 4, "%d", int(program_number+1));
1048 add_pgm_change(program_change, buf);
1054 MidiRegionView::display_sysexes()
1056 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1057 Evoral::MusicalTime time = (*i)->time();
1062 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1063 str << int((*i)->buffer()[b]);
1064 if (b != (*i)->size() -1) {
1068 string text = str.str();
1070 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1072 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1074 double height = midi_stream_view()->contents_height();
1076 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1077 new CanvasSysEx(*this, *group, text, height, x, 1.0));
1079 // Show unless program change is beyond the region bounds
1080 if (time - _region->start() >= _region->length() || time < _region->start()) {
1086 _sys_exes.push_back(sysex);
1091 MidiRegionView::~MidiRegionView ()
1093 in_destructor = true;
1095 trackview.editor().hide_verbose_canvas_cursor ();
1097 note_delete_connection.disconnect ();
1099 delete _list_editor;
1101 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1103 if (_active_notes) {
1111 delete _diff_command;
1112 delete _step_edit_cursor;
1116 MidiRegionView::region_resized (const PropertyChange& what_changed)
1118 RegionView::region_resized(what_changed);
1120 if (what_changed.contains (ARDOUR::Properties::position)) {
1121 set_duration(_region->length(), 0);
1122 if (_enable_display) {
1129 MidiRegionView::reset_width_dependent_items (double pixel_width)
1131 RegionView::reset_width_dependent_items(pixel_width);
1132 assert(_pixel_width == pixel_width);
1134 if (_enable_display) {
1138 move_step_edit_cursor (_step_edit_cursor_position);
1139 set_step_edit_cursor_width (_step_edit_cursor_width);
1143 MidiRegionView::set_height (double height)
1145 static const double FUDGE = 2.0;
1146 const double old_height = _height;
1147 RegionView::set_height(height);
1148 _height = height - FUDGE;
1150 apply_note_range(midi_stream_view()->lowest_note(),
1151 midi_stream_view()->highest_note(),
1152 height != old_height + FUDGE);
1155 name_pixbuf->raise_to_top();
1158 for (PgmChanges::iterator x = _pgm_changes.begin(); x != _pgm_changes.end(); ++x) {
1159 (*x)->set_height (midi_stream_view()->contents_height());
1162 if (_step_edit_cursor) {
1163 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1168 /** Apply the current note range from the stream view
1169 * by repositioning/hiding notes as necessary
1172 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1174 if (!_enable_display) {
1178 if (!force && _current_range_min == min && _current_range_max == max) {
1182 _current_range_min = min;
1183 _current_range_max = max;
1185 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1186 CanvasNoteEvent* event = *i;
1187 boost::shared_ptr<NoteType> note (event->note());
1189 if (note->note() < _current_range_min ||
1190 note->note() > _current_range_max) {
1196 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1198 const double y1 = midi_stream_view()->note_to_y(note->note());
1199 const double y2 = y1 + floor(midi_stream_view()->note_height());
1201 cnote->property_y1() = y1;
1202 cnote->property_y2() = y2;
1204 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1206 double x = trackview.editor().frame_to_pixel(
1207 beats_to_frames(note->time()) - _region->start());
1208 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1209 double y = midi_stream_view()->note_to_y(event->note()->note())
1210 + ((diamond_size-2.0) / 4.0);
1212 chit->set_height (diamond_size);
1213 chit->move (x - chit->x1(), y - chit->y1());
1220 MidiRegionView::add_ghost (TimeAxisView& tv)
1224 double unit_position = _region->position () / samples_per_unit;
1225 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1226 MidiGhostRegion* ghost;
1228 if (mtv && mtv->midi_view()) {
1229 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1230 to allow having midi notes on top of note lines and waveforms.
1232 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1234 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1237 ghost->set_height ();
1238 ghost->set_duration (_region->length() / samples_per_unit);
1239 ghosts.push_back (ghost);
1241 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1242 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1243 ghost->add_note(note);
1247 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1253 /** Begin tracking note state for successive calls to add_event
1256 MidiRegionView::begin_write()
1258 assert(!_active_notes);
1259 _active_notes = new CanvasNote*[128];
1260 for (unsigned i=0; i < 128; ++i) {
1261 _active_notes[i] = 0;
1266 /** Destroy note state for add_event
1269 MidiRegionView::end_write()
1271 delete[] _active_notes;
1273 _marked_for_selection.clear();
1274 _marked_for_velocity.clear();
1278 /** Resolve an active MIDI note (while recording).
1281 MidiRegionView::resolve_note(uint8_t note, double end_time)
1283 if (midi_view()->note_mode() != Sustained) {
1287 if (_active_notes && _active_notes[note]) {
1288 const framepos_t end_time_frames = beats_to_frames(end_time);
1289 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1290 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1291 _active_notes[note] = 0;
1296 /** Extend active notes to rightmost edge of region (if length is changed)
1299 MidiRegionView::extend_active_notes()
1301 if (!_active_notes) {
1305 for (unsigned i=0; i < 128; ++i) {
1306 if (_active_notes[i]) {
1307 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1314 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1316 if (no_sound_notes || !trackview.editor().sound_notes()) {
1320 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1322 if (!route_ui || !route_ui->midi_track()) {
1326 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1332 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1334 if (no_sound_notes || !trackview.editor().sound_notes()) {
1338 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1340 if (!route_ui || !route_ui->midi_track()) {
1344 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1346 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1355 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1357 const framepos_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 framepos_t note_start_frames = beats_to_frames(note->time());
1375 /* trim note display to not overlap the end of its region */
1376 const framepos_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 framepos_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 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1499 framepos_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 = Evoral::MaxMusicalTime;
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 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
1965 PossibleChord to_play;
1966 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1968 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1969 if ((*i)->note()->time() < earliest) {
1970 earliest = (*i)->note()->time();
1974 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1975 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
1976 to_play.push_back ((*i)->note());
1978 (*i)->move_event(dx, dy);
1981 if (dy && !_selection.empty() && !no_sound_notes && trackview.editor().sound_notes()) {
1983 if (to_play.size() > 1) {
1985 PossibleChord shifted;
1987 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
1988 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
1989 moved_note->set_note (moved_note->note() + cumulative_dy);
1990 shifted.push_back (moved_note);
1993 play_midi_chord (shifted);
1995 } else if (!to_play.empty()) {
1997 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
1998 moved_note->set_note (moved_note->note() + cumulative_dy);
1999 play_midi_note (moved_note);
2005 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2007 assert (!_selection.empty());
2009 uint8_t lowest_note_in_selection = 127;
2010 uint8_t highest_note_in_selection = 0;
2011 uint8_t highest_note_difference = 0;
2013 // find highest and lowest notes first
2015 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2016 uint8_t pitch = (*i)->note()->note();
2017 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2018 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2022 cerr << "dnote: " << (int) dnote << endl;
2023 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2024 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2025 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2026 << int(highest_note_in_selection) << endl;
2027 cerr << "selection size: " << _selection.size() << endl;
2028 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2031 // Make sure the note pitch does not exceed the MIDI standard range
2032 if (highest_note_in_selection + dnote > 127) {
2033 highest_note_difference = highest_note_in_selection - 127;
2036 start_diff_command(_("move notes"));
2038 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2040 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2046 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
2048 uint8_t original_pitch = (*i)->note()->note();
2049 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2051 // keep notes in standard midi range
2052 clamp_to_0_127(new_pitch);
2054 // keep original pitch if note is dragged outside valid midi range
2055 if ((original_pitch != 0 && new_pitch == 0)
2056 || (original_pitch != 127 && new_pitch == 127)) {
2057 new_pitch = original_pitch;
2060 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2061 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2063 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
2068 // care about notes being moved beyond the upper/lower bounds on the canvas
2069 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2070 highest_note_in_selection > midi_stream_view()->highest_note()) {
2071 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2076 MidiRegionView::snap_pixel_to_frame(double x)
2078 PublicEditor& editor = trackview.editor();
2079 // x is region relative, convert it to global absolute frames
2080 framepos_t frame = editor.pixel_to_frame(x) + _region->position();
2081 editor.snap_to(frame);
2082 return frame - _region->position(); // convert back to region relative
2086 MidiRegionView::snap_frame_to_frame(framepos_t x)
2088 PublicEditor& editor = trackview.editor();
2089 // x is region relative, convert it to global absolute frames
2090 framepos_t frame = x + _region->position();
2091 editor.snap_to(frame);
2092 return frame - _region->position(); // convert back to region relative
2096 MidiRegionView::snap_to_pixel(double x)
2098 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2102 MidiRegionView::get_position_pixels()
2104 framepos_t region_frame = get_position();
2105 return trackview.editor().frame_to_pixel(region_frame);
2109 MidiRegionView::get_end_position_pixels()
2111 framepos_t frame = get_position() + get_duration ();
2112 return trackview.editor().frame_to_pixel(frame);
2116 MidiRegionView::beats_to_frames(double beats) const
2118 return _time_converter.to(beats);
2122 MidiRegionView::frames_to_beats(framepos_t frames) const
2124 return _time_converter.from(frames);
2128 MidiRegionView::begin_resizing (bool /*at_front*/)
2130 _resize_data.clear();
2132 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2133 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2135 // only insert CanvasNotes into the map
2137 NoteResizeData *resize_data = new NoteResizeData();
2138 resize_data->canvas_note = note;
2140 // create a new SimpleRect from the note which will be the resize preview
2141 SimpleRect *resize_rect = new SimpleRect(
2142 *group, note->x1(), note->y1(), note->x2(), note->y2());
2144 // calculate the colors: get the color settings
2145 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2146 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2149 // make the resize preview notes more transparent and bright
2150 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2152 // calculate color based on note velocity
2153 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2154 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2158 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2159 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2161 resize_data->resize_rect = resize_rect;
2162 _resize_data.push_back(resize_data);
2167 /** Update resizing notes while user drags.
2168 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2169 * @param at_front which end of the note (true == note on, false == note off)
2170 * @param delta_x change in mouse position since the start of the drag
2171 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2172 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2173 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2174 * as the \a primary note.
2177 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2179 bool cursor_set = false;
2181 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2182 SimpleRect* resize_rect = (*i)->resize_rect;
2183 CanvasNote* canvas_note = (*i)->canvas_note;
2188 current_x = canvas_note->x1() + delta_x;
2190 current_x = primary->x1() + delta_x;
2194 current_x = canvas_note->x2() + delta_x;
2196 current_x = primary->x2() + delta_x;
2201 resize_rect->property_x1() = snap_to_pixel(current_x);
2202 resize_rect->property_x2() = canvas_note->x2();
2204 resize_rect->property_x2() = snap_to_pixel(current_x);
2205 resize_rect->property_x1() = canvas_note->x1();
2211 beats = snap_pixel_to_frame (current_x);
2212 beats = frames_to_beats (beats);
2217 if (beats < canvas_note->note()->end_time()) {
2218 len = canvas_note->note()->time() - beats;
2219 len += canvas_note->note()->length();
2224 if (beats >= canvas_note->note()->end_time()) {
2225 len = beats - canvas_note->note()->time();
2232 snprintf (buf, sizeof (buf), "%.3g beats", len);
2233 trackview.editor().show_verbose_canvas_cursor_with (buf);
2242 /** Finish resizing notes when the user releases the mouse button.
2243 * Parameters the same as for \a update_resizing().
2246 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2248 start_diff_command(_("resize notes"));
2250 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2251 CanvasNote* canvas_note = (*i)->canvas_note;
2252 SimpleRect* resize_rect = (*i)->resize_rect;
2257 current_x = canvas_note->x1() + delta_x;
2259 current_x = primary->x1() + delta_x;
2263 current_x = canvas_note->x2() + delta_x;
2265 current_x = primary->x2() + delta_x;
2269 current_x = snap_pixel_to_frame (current_x);
2270 current_x = frames_to_beats (current_x);
2272 if (at_front && current_x < canvas_note->note()->end_time()) {
2273 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2275 double len = canvas_note->note()->time() - current_x;
2276 len += canvas_note->note()->length();
2279 /* XXX convert to beats */
2280 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2285 double len = current_x - canvas_note->note()->time();
2288 /* XXX convert to beats */
2289 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2297 _resize_data.clear();
2302 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2304 diff_add_change (event, MidiModel::DiffCommand::Channel, (uint8_t) channel);
2308 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2310 uint8_t new_velocity;
2313 new_velocity = event->note()->velocity() + velocity;
2314 clamp_to_0_127(new_velocity);
2316 new_velocity = velocity;
2319 event->set_selected (event->selected()); // change color
2321 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2325 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2330 new_note = event->note()->note() + note;
2335 clamp_to_0_127 (new_note);
2336 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2340 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2342 bool change_start = false;
2343 bool change_length = false;
2344 Evoral::MusicalTime new_start = 0;
2345 Evoral::MusicalTime new_length = 0;
2347 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2349 front_delta: if positive - move the start of the note later in time (shortening it)
2350 if negative - move the start of the note earlier in time (lengthening it)
2352 end_delta: if positive - move the end of the note later in time (lengthening it)
2353 if negative - move the end of the note earlier in time (shortening it)
2357 if (front_delta < 0) {
2359 if (event->note()->time() < -front_delta) {
2362 new_start = event->note()->time() + front_delta; // moves earlier
2365 /* start moved toward zero, so move the end point out to where it used to be.
2366 Note that front_delta is negative, so this increases the length.
2369 new_length = event->note()->length() - front_delta;
2370 change_start = true;
2371 change_length = true;
2375 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2377 if (new_pos < event->note()->end_time()) {
2378 new_start = event->note()->time() + front_delta;
2379 /* start moved toward the end, so move the end point back to where it used to be */
2380 new_length = event->note()->length() - front_delta;
2381 change_start = true;
2382 change_length = true;
2389 bool can_change = true;
2390 if (end_delta < 0) {
2391 if (event->note()->length() < -end_delta) {
2397 new_length = event->note()->length() + end_delta;
2398 change_length = true;
2403 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2406 if (change_length) {
2407 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2412 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2414 Evoral::MusicalTime new_time;
2418 if (event->note()->time() < -delta) {
2421 new_time = event->note()->time() + delta;
2424 new_time = event->note()->time() + delta;
2430 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2434 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2436 diff_add_change (event, MidiModel::DiffCommand::Length, t);
2440 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2444 if (_selection.empty()) {
2459 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2460 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2466 start_diff_command(_("change velocities"));
2468 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2469 Selection::iterator next = i;
2471 change_note_velocity (*i, delta, true);
2477 if (!_selection.empty()) {
2479 snprintf (buf, sizeof (buf), "Vel %d",
2480 (int) (*_selection.begin())->note()->velocity());
2481 trackview.editor().show_verbose_canvas_cursor_with (buf);
2487 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2489 if (_selection.empty()) {
2506 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2508 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2512 if ((int8_t) (*i)->note()->note() + delta > 127) {
2519 start_diff_command (_("transpose"));
2521 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2522 Selection::iterator next = i;
2524 change_note_note (*i, delta, true);
2532 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2538 /* grab the current grid distance */
2540 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2542 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2543 cerr << "Grid type not available as beats - TO BE FIXED\n";
2553 start_diff_command (_("change note lengths"));
2555 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2556 Selection::iterator next = i;
2559 /* note the negation of the delta for start */
2561 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2570 MidiRegionView::nudge_notes (bool forward)
2572 if (_selection.empty()) {
2576 /* pick a note as the point along the timeline to get the nudge distance.
2577 its not necessarily the earliest note, so we may want to pull the notes out
2578 into a vector and sort before using the first one.
2581 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2583 framepos_t distance;
2585 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2587 /* grid is off - use nudge distance */
2589 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2595 framepos_t next_pos = ref_point;
2598 if (max_framepos - 1 < next_pos) {
2602 if (next_pos == 0) {
2608 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2609 distance = ref_point - next_pos;
2612 if (distance == 0) {
2616 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2622 start_diff_command (_("nudge"));
2624 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2625 Selection::iterator next = i;
2627 change_note_time (*i, delta, true);
2635 MidiRegionView::change_channel(uint8_t channel)
2637 start_diff_command(_("change channel"));
2638 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2639 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2647 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2649 if (_mouse_state == SelectTouchDragging) {
2650 note_selected (ev, true);
2653 show_verbose_canvas_cursor (ev->note ());
2657 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent* note)
2659 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2660 (*i)->hide_velocity ();
2663 trackview.editor().hide_verbose_canvas_cursor ();
2667 MidiRegionView::note_motion (float fraction)
2669 cerr << "Now at " << fraction << " within note\n";
2673 MidiRegionView::switch_source(boost::shared_ptr<Source> src)
2675 boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(src);
2677 display_model(msrc->model());
2681 MidiRegionView::set_frame_color()
2684 if (_selected && should_show_selection) {
2685 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2687 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2693 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2697 case FilterChannels:
2698 _force_channel = -1;
2701 _force_channel = mask;
2702 mask = 0xFFFF; // Show all notes as active (below)
2705 // Update notes for selection
2706 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2707 (*i)->on_channel_selection_change(mask);
2710 _last_channel_selection = mask;
2714 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2716 _model_name = model;
2717 _custom_device_mode = custom_device_mode;
2722 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2724 if (_selection.empty()) {
2728 PublicEditor& editor (trackview.editor());
2733 editor.get_cut_buffer().add (selection_as_cut_buffer());
2741 start_diff_command();
2743 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2749 diff_remove_note (*i);
2759 MidiRegionView::selection_as_cut_buffer () const
2763 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2764 NoteType* n = (*i)->note().get();
2765 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2768 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2775 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
2781 start_diff_command (_("paste"));
2783 Evoral::MusicalTime beat_delta;
2784 Evoral::MusicalTime paste_pos_beats;
2785 Evoral::MusicalTime duration;
2786 Evoral::MusicalTime end_point = 0;
2788 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2789 paste_pos_beats = frames_to_beats (pos - _region->position());
2790 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2791 paste_pos_beats = 0;
2795 for (int n = 0; n < (int) times; ++n) {
2797 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2799 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2800 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2802 /* make all newly added notes selected */
2804 diff_add_note (copied_note, true);
2805 end_point = copied_note->end_time();
2808 paste_pos_beats += duration;
2811 /* if we pasted past the current end of the region, extend the region */
2813 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
2814 framepos_t region_end = _region->position() + _region->length() - 1;
2816 if (end_frame > region_end) {
2818 trackview.session()->begin_reversible_command (_("paste"));
2820 _region->clear_changes ();
2821 _region->set_length (end_frame, this);
2822 trackview.session()->add_command (new StatefulDiffCommand (_region));
2828 struct EventNoteTimeEarlyFirstComparator {
2829 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2830 return a->note()->time() < b->note()->time();
2835 MidiRegionView::time_sort_events ()
2837 if (!_sort_needed) {
2841 EventNoteTimeEarlyFirstComparator cmp;
2844 _sort_needed = false;
2848 MidiRegionView::goto_next_note ()
2850 // framepos_t pos = -1;
2851 bool use_next = false;
2853 if (_events.back()->selected()) {
2857 time_sort_events ();
2859 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2860 if ((*i)->selected()) {
2863 } else if (use_next) {
2865 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2870 /* use the first one */
2872 unique_select (_events.front());
2877 MidiRegionView::goto_previous_note ()
2879 // framepos_t pos = -1;
2880 bool use_next = false;
2882 if (_events.front()->selected()) {
2886 time_sort_events ();
2888 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2889 if ((*i)->selected()) {
2892 } else if (use_next) {
2894 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2899 /* use the last one */
2901 unique_select (*(_events.rbegin()));
2905 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2907 bool had_selected = false;
2909 time_sort_events ();
2911 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2912 if ((*i)->selected()) {
2913 selected.insert ((*i)->note());
2914 had_selected = true;
2918 if (allow_all_if_none_selected && !had_selected) {
2919 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2920 selected.insert ((*i)->note());
2926 MidiRegionView::update_ghost_note (double x, double y)
2932 framepos_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
2933 trackview.editor().snap_to (f);
2934 f -= _region->position ();
2937 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
2942 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
2944 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
2945 _ghost_note->note()->set_length (length);
2946 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
2948 update_note (_ghost_note);
2950 show_verbose_canvas_cursor (_ghost_note->note ());
2954 MidiRegionView::create_ghost_note (double x, double y)
2959 boost::shared_ptr<NoteType> g (new NoteType);
2960 _ghost_note = new NoEventCanvasNote (*this, *group, g);
2961 update_ghost_note (x, y);
2962 _ghost_note->show ();
2967 show_verbose_canvas_cursor (_ghost_note->note ());
2971 MidiRegionView::snap_changed ()
2977 create_ghost_note (_last_ghost_x, _last_ghost_y);
2981 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
2984 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
2985 Evoral::midi_note_name (n->note()).c_str(),
2987 (int) n->velocity());
2988 trackview.editor().show_verbose_canvas_cursor_with (buf);
2992 MidiRegionView::drop_down_keys ()
2994 _mouse_state = None;
2998 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double x, double y)
3000 double note = midi_stream_view()->y_to_note(y);
3002 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3004 cerr << "Selecting by position\n";
3006 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3008 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3009 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3010 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3011 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3016 bool add_mrv_selection = false;
3018 if (_selection.empty()) {
3019 add_mrv_selection = true;
3022 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3023 if (_selection.insert (*i).second) {
3024 (*i)->set_selected (true);
3028 if (add_mrv_selection) {
3029 PublicEditor& editor (trackview.editor());
3030 editor.get_selection().add (this);
3035 MidiRegionView::color_handler ()
3037 RegionView::color_handler ();
3039 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3040 (*i)->set_selected ((*i)->selected()); // will change color
3043 /* XXX probably more to do here */
3047 MidiRegionView::enable_display (bool yn)
3049 RegionView::enable_display (yn);
3056 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3058 if (_step_edit_cursor == 0) {
3059 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3061 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3062 _step_edit_cursor->property_y1() = 0;
3063 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3064 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3065 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3068 move_step_edit_cursor (pos);
3069 _step_edit_cursor->show ();
3073 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3075 _step_edit_cursor_position = pos;
3077 if (_step_edit_cursor) {
3078 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3079 _step_edit_cursor->property_x1() = pixel;
3080 set_step_edit_cursor_width (_step_edit_cursor_width);
3085 MidiRegionView::hide_step_edit_cursor ()
3087 if (_step_edit_cursor) {
3088 _step_edit_cursor->hide ();
3093 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3095 _step_edit_cursor_width = beats;
3097 if (_step_edit_cursor) {
3098 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));