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)
95 , _earliest_selected_time (Evoral::MaxMusicalTime)
99 , _optimization_iterator (_events.end())
101 , no_sound_notes (false)
103 _note_group->raise_to_top();
104 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
107 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
108 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
109 TimeAxisViewItem::Visibility visibility)
110 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
112 , _last_channel_selection(0xFFFF)
113 , _model_name(string())
114 , _custom_device_mode(string())
116 , _note_group(new ArdourCanvas::Group(*parent))
120 , _earliest_selected_time (Evoral::MaxMusicalTime)
123 , _sort_needed (true)
124 , _optimization_iterator (_events.end())
126 , no_sound_notes (false)
128 _note_group->raise_to_top();
129 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
132 MidiRegionView::MidiRegionView (const MidiRegionView& other)
133 : sigc::trackable(other)
136 , _last_channel_selection(0xFFFF)
137 , _model_name(string())
138 , _custom_device_mode(string())
140 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
144 , _earliest_selected_time (Evoral::MaxMusicalTime)
147 , _sort_needed (true)
148 , _optimization_iterator (_events.end())
150 , no_sound_notes (false)
155 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
156 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
161 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
162 : RegionView (other, boost::shared_ptr<Region> (region))
164 , _last_channel_selection(0xFFFF)
165 , _model_name(string())
166 , _custom_device_mode(string())
168 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
172 , _earliest_selected_time (Evoral::MaxMusicalTime)
175 , _sort_needed (true)
176 , _optimization_iterator (_events.end())
178 , no_sound_notes (false)
183 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
184 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
190 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
192 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
194 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
195 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
199 midi_region()->midi_source(0)->load_model();
202 _model = midi_region()->midi_source(0)->model();
203 _enable_display = false;
205 RegionView::init (basic_color, false);
207 compute_colors (basic_color);
209 set_height (trackview.current_height());
212 region_sync_changed ();
213 region_resized (ARDOUR::bounds_change);
216 reset_width_dependent_items (_pixel_width);
220 _enable_display = true;
223 display_model (_model);
227 group->raise_to_top();
228 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
230 midi_view()->signal_channel_mode_changed().connect(
231 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
233 midi_view()->signal_midi_patch_settings_changed().connect(
234 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
236 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
240 MidiRegionView::canvas_event(GdkEvent* ev)
242 if (!trackview.editor().internal_editing()) {
246 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
247 to its items, which means that ev->type == GDK_SCROLL will never be seen
252 return scroll (&ev->scroll);
255 return key_press (&ev->key);
257 case GDK_KEY_RELEASE:
258 return key_release (&ev->key);
260 case GDK_BUTTON_PRESS:
261 return button_press (&ev->button);
263 case GDK_2BUTTON_PRESS:
266 case GDK_BUTTON_RELEASE:
267 return button_release (&ev->button);
269 case GDK_ENTER_NOTIFY:
270 return enter_notify (&ev->crossing);
272 case GDK_LEAVE_NOTIFY:
273 return leave_notify (&ev->crossing);
275 case GDK_MOTION_NOTIFY:
276 return motion (&ev->motion);
286 MidiRegionView::enter_notify (GdkEventCrossing* ev)
288 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
290 Keyboard::magic_widget_grab_focus();
293 if (trackview.editor().current_mouse_mode() == MouseRange) {
294 create_ghost_note (ev->x, ev->y);
301 MidiRegionView::leave_notify (GdkEventCrossing* ev)
303 trackview.editor().hide_verbose_canvas_cursor ();
310 MidiRegionView::button_press (GdkEventButton* ev)
314 group->w2i (_last_x, _last_y);
316 if (_mouse_state != SelectTouchDragging && ev->button == 1) {
317 _pressed_button = ev->button;
318 _mouse_state = Pressed;
322 _pressed_button = ev->button;
328 MidiRegionView::button_release (GdkEventButton* ev)
330 double event_x, event_y;
331 nframes64_t event_frame = 0;
335 group->w2i(event_x, event_y);
336 group->ungrab(ev->time);
337 event_frame = trackview.editor().pixel_to_frame(event_x);
339 if (ev->button == 3) {
341 } else if (_pressed_button != 1) {
345 switch (_mouse_state) {
346 case Pressed: // Clicked
347 switch (trackview.editor().current_mouse_mode()) {
351 maybe_select_by_position (ev, event_x, event_y);
357 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
361 create_note_at (event_x, event_y, beats, true);
369 case SelectRectDragging: // Select drag done
375 case AddDragging: // Add drag done
377 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
378 const double x = _drag_rect->property_x1();
379 const double length = trackview.editor().pixel_to_frame
380 (_drag_rect->property_x2() - _drag_rect->property_x1());
382 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), false);
388 create_ghost_note (ev->x, ev->y);
398 MidiRegionView::motion (GdkEventMotion* ev)
400 double event_x, event_y;
401 nframes64_t event_frame = 0;
405 group->w2i(event_x, event_y);
407 // convert event_x to global frame
408 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
409 trackview.editor().snap_to(event_frame);
410 // convert event_frame back to local coordinates relative to position
411 event_frame -= _region->position();
414 update_ghost_note (ev->x, ev->y);
417 /* any motion immediately hides velocity text that may have been visible */
419 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
420 (*i)->hide_velocity ();
423 switch (_mouse_state) {
424 case Pressed: // Maybe start a drag, if we've moved a bit
426 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
427 /* no appreciable movement since the button was pressed */
432 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
433 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
434 Gdk::Cursor(Gdk::FLEUR), ev->time);
437 _drag_start_x = event_x;
438 _drag_start_y = event_y;
440 _drag_rect = new ArdourCanvas::SimpleRect(*group);
441 _drag_rect->property_x1() = event_x;
442 _drag_rect->property_y1() = event_y;
443 _drag_rect->property_x2() = event_x;
444 _drag_rect->property_y2() = event_y;
445 _drag_rect->property_outline_what() = 0xFF;
446 _drag_rect->property_outline_color_rgba()
447 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
448 _drag_rect->property_fill_color_rgba()
449 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
451 _mouse_state = SelectRectDragging;
454 // Add note drag start
455 } else if (trackview.editor().internal_editing()) {
460 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
461 Gdk::Cursor(Gdk::FLEUR), ev->time);
464 _drag_start_x = event_x;
465 _drag_start_y = event_y;
467 _drag_rect = new ArdourCanvas::SimpleRect(*group);
468 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
470 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
471 midi_stream_view()->y_to_note(event_y));
472 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
473 _drag_rect->property_y2() = _drag_rect->property_y1()
474 + floor(midi_stream_view()->note_height());
475 _drag_rect->property_outline_what() = 0xFF;
476 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
477 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
479 _mouse_state = AddDragging;
485 case SelectRectDragging: // Select drag motion
486 case AddDragging: // Add note drag motion
490 GdkModifierType state;
491 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
496 if (_mouse_state == AddDragging)
497 event_x = trackview.editor().frame_to_pixel(event_frame);
500 if (event_x > _drag_start_x)
501 _drag_rect->property_x2() = event_x;
503 _drag_rect->property_x1() = event_x;
506 if (_drag_rect && _mouse_state == SelectRectDragging) {
507 if (event_y > _drag_start_y)
508 _drag_rect->property_y2() = event_y;
510 _drag_rect->property_y1() = event_y;
512 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
518 case SelectTouchDragging:
530 MidiRegionView::scroll (GdkEventScroll* ev)
532 if (_selection.empty()) {
536 trackview.editor().hide_verbose_canvas_cursor ();
538 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
540 if (ev->direction == GDK_SCROLL_UP) {
541 change_velocities (true, fine, false);
542 } else if (ev->direction == GDK_SCROLL_DOWN) {
543 change_velocities (false, fine, false);
549 MidiRegionView::key_press (GdkEventKey* ev)
551 /* since GTK bindings are generally activated on press, and since
552 detectable auto-repeat is the name of the game and only sends
553 repeated presses, carry out key actions at key press, not release.
556 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
557 _mouse_state = SelectTouchDragging;
560 } else if (ev->keyval == GDK_Escape) {
564 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
566 bool start = (ev->keyval == GDK_comma);
567 bool end = (ev->keyval == GDK_period);
568 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
569 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
571 change_note_lengths (fine, shorter, 0.0, start, end);
575 } else if (ev->keyval == GDK_Delete) {
580 } else if (ev->keyval == GDK_Tab) {
582 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
583 goto_previous_note ();
589 } else if (ev->keyval == GDK_Up) {
591 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
592 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
594 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
595 change_velocities (true, fine, allow_smush);
597 transpose (true, fine, allow_smush);
601 } else if (ev->keyval == GDK_Down) {
603 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
604 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
606 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
607 change_velocities (false, fine, allow_smush);
609 transpose (false, fine, allow_smush);
613 } else if (ev->keyval == GDK_Left) {
618 } else if (ev->keyval == GDK_Right) {
623 } else if (ev->keyval == GDK_Control_L) {
632 MidiRegionView::key_release (GdkEventKey* ev)
634 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
642 MidiRegionView::show_list_editor ()
645 _list_editor = new MidiListEditor (trackview.session(), midi_region());
647 _list_editor->present ();
650 /** Add a note to the model, and the view, at a canvas (click) coordinate.
651 * \param x horizontal position in pixels
652 * \param y vertical position in pixels
653 * \param length duration of the note in beats, which will be snapped to the grid
654 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
657 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
659 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
660 MidiStreamView* const view = mtv->midi_view();
662 double note = midi_stream_view()->y_to_note(y);
665 assert(note <= 127.0);
667 // Start of note in frames relative to region start
668 nframes64_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
669 assert(start_frames >= 0);
672 length = frames_to_beats(
673 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
675 assert (length != 0);
678 length = frames_to_beats (beats_to_frames (length) - 1);
681 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
685 /* pick the highest selected channel, unless all channels are selected,
686 which is interpreted to mean channel 1 (zero)
689 for (uint16_t i = 0; i < 16; ++i) {
690 if (chn_mask & (1<<i)) {
700 const boost::shared_ptr<NoteType> new_note (new NoteType (channel,
701 frames_to_beats(start_frames + _region->start()), length,
702 (uint8_t)note, 0x40));
704 if (_model->contains (new_note)) {
708 view->update_note_range(new_note->note());
710 MidiModel::DiffCommand* cmd = _model->new_diff_command("add note");
712 _model->apply_command(*trackview.session(), cmd);
714 play_midi_note (new_note);
718 MidiRegionView::clear_events()
723 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
724 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
729 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
734 _pgm_changes.clear();
736 _optimization_iterator = _events.end();
741 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
744 content_connection.disconnect ();
745 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
749 if (_enable_display) {
755 MidiRegionView::start_diff_command(string name)
757 if (!_diff_command) {
758 _diff_command = _model->new_diff_command(name);
763 MidiRegionView::diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
766 _diff_command->add(note);
769 _marked_for_selection.insert(note);
772 _marked_for_velocity.insert(note);
777 MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
779 if (_diff_command && ev->note()) {
780 _diff_command->remove(ev->note());
785 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
786 MidiModel::DiffCommand::Property property,
790 _diff_command->change (ev->note(), property, val);
795 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
796 MidiModel::DiffCommand::Property property,
797 Evoral::MusicalTime val)
800 _diff_command->change (ev->note(), property, val);
805 MidiRegionView::apply_diff ()
809 if (!_diff_command) {
813 if ((add_or_remove = _diff_command->adds_or_removes())) {
814 // Mark all selected notes for selection when model reloads
815 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
816 _marked_for_selection.insert((*i)->note());
820 _model->apply_command(*trackview.session(), _diff_command);
822 midi_view()->midi_track()->playlist_modified();
825 _marked_for_selection.clear();
828 _marked_for_velocity.clear();
832 MidiRegionView::apply_diff_as_subcommand()
836 if (!_diff_command) {
840 if ((add_or_remove = _diff_command->adds_or_removes())) {
841 // Mark all selected notes for selection when model reloads
842 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
843 _marked_for_selection.insert((*i)->note());
847 _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
849 midi_view()->midi_track()->playlist_modified();
852 _marked_for_selection.clear();
854 _marked_for_velocity.clear();
859 MidiRegionView::abort_command()
861 delete _diff_command;
867 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
869 if (_optimization_iterator != _events.end()) {
870 ++_optimization_iterator;
873 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
874 return *_optimization_iterator;
877 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
878 if ((*_optimization_iterator)->note() == note) {
879 return *_optimization_iterator;
887 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
889 MidiModel::Notes notes;
890 _model->get_notes (notes, op, val, chan_mask);
892 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
893 CanvasNoteEvent* cne = find_canvas_note (*n);
901 MidiRegionView::redisplay_model()
903 // Don't redisplay the model if we're currently recording and displaying that
909 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
913 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
917 MidiModel::ReadLock lock(_model->read_lock());
919 MidiModel::Notes& notes (_model->notes());
920 _optimization_iterator = _events.begin();
922 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
924 boost::shared_ptr<NoteType> note (*n);
925 CanvasNoteEvent* cne;
928 if (note_in_region_range (note, visible)) {
930 if ((cne = find_canvas_note (note)) != 0) {
937 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
939 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
951 add_note (note, visible);
956 if ((cne = find_canvas_note (note)) != 0) {
964 /* remove note items that are no longer valid */
966 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
967 if (!(*i)->valid ()) {
969 i = _events.erase (i);
975 _pgm_changes.clear();
979 display_program_changes();
981 _marked_for_selection.clear ();
982 _marked_for_velocity.clear ();
984 /* we may have caused _events to contain things out of order (e.g. if a note
985 moved earlier or later). we don't generally need them in time order, but
986 make a note that a sort is required for those cases that require it.
993 MidiRegionView::display_program_changes()
995 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
996 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
998 for (uint8_t i = 0; i < 16; ++i) {
999 if (chn_mask & (1<<i)) {
1000 display_program_changes_on_channel (i);
1006 MidiRegionView::display_program_changes_on_channel(uint8_t channel)
1008 boost::shared_ptr<Evoral::Control> control =
1009 _model->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation, channel));
1015 Glib::Mutex::Lock lock (control->list()->lock());
1017 for (AutomationList::const_iterator event = control->list()->begin();
1018 event != control->list()->end(); ++event) {
1019 double event_time = (*event)->when;
1020 double program_number = floor((*event)->value + 0.5);
1022 // Get current value of bank select MSB at time of the program change
1023 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1024 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1026 if (msb_control != 0) {
1027 msb = uint8_t(floor(msb_control->get_double(true, event_time) + 0.5));
1030 // Get current value of bank select LSB at time of the program change
1031 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1032 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1034 if (lsb_control != 0) {
1035 lsb = uint8_t(floor(lsb_control->get_double(true, event_time) + 0.5));
1038 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
1040 boost::shared_ptr<MIDI::Name::Patch> patch =
1041 MIDI::Name::MidiPatchManager::instance().find_patch(
1042 _model_name, _custom_device_mode, channel, patch_key);
1044 PCEvent program_change(event_time, uint8_t(program_number), channel);
1047 add_pgm_change(program_change, patch->name());
1050 // program_number is zero-based: convert to one-based
1051 snprintf(buf, 4, "%d", int(program_number+1));
1052 add_pgm_change(program_change, buf);
1058 MidiRegionView::display_sysexes()
1060 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1061 Evoral::MusicalTime time = (*i)->time();
1066 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1067 str << int((*i)->buffer()[b]);
1068 if (b != (*i)->size() -1) {
1072 string text = str.str();
1074 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1076 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1078 double height = midi_stream_view()->contents_height();
1080 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1081 new CanvasSysEx(*this, *group, text, height, x, 1.0));
1083 // Show unless program change is beyond the region bounds
1084 if (time - _region->start() >= _region->length() || time < _region->start()) {
1090 _sys_exes.push_back(sysex);
1095 MidiRegionView::~MidiRegionView ()
1097 in_destructor = true;
1099 trackview.editor().hide_verbose_canvas_cursor ();
1101 note_delete_connection.disconnect ();
1103 delete _list_editor;
1105 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1107 if (_active_notes) {
1115 delete _diff_command;
1116 delete _step_edit_cursor;
1120 MidiRegionView::region_resized (const PropertyChange& what_changed)
1122 RegionView::region_resized(what_changed);
1124 if (what_changed.contains (ARDOUR::Properties::position)) {
1125 set_duration(_region->length(), 0);
1126 if (_enable_display) {
1133 MidiRegionView::reset_width_dependent_items (double pixel_width)
1135 RegionView::reset_width_dependent_items(pixel_width);
1136 assert(_pixel_width == pixel_width);
1138 if (_enable_display) {
1142 move_step_edit_cursor (_step_edit_cursor_position);
1143 set_step_edit_cursor_width (_step_edit_cursor_width);
1147 MidiRegionView::set_height (double height)
1149 static const double FUDGE = 2.0;
1150 const double old_height = _height;
1151 RegionView::set_height(height);
1152 _height = height - FUDGE;
1154 apply_note_range(midi_stream_view()->lowest_note(),
1155 midi_stream_view()->highest_note(),
1156 height != old_height + FUDGE);
1159 name_pixbuf->raise_to_top();
1162 for (PgmChanges::iterator x = _pgm_changes.begin(); x != _pgm_changes.end(); ++x) {
1163 (*x)->set_height (midi_stream_view()->contents_height());
1166 if (_step_edit_cursor) {
1167 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1172 /** Apply the current note range from the stream view
1173 * by repositioning/hiding notes as necessary
1176 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1178 if (!_enable_display) {
1182 if (!force && _current_range_min == min && _current_range_max == max) {
1186 _current_range_min = min;
1187 _current_range_max = max;
1189 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1190 CanvasNoteEvent* event = *i;
1191 boost::shared_ptr<NoteType> note (event->note());
1193 if (note->note() < _current_range_min ||
1194 note->note() > _current_range_max) {
1200 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1202 const double y1 = midi_stream_view()->note_to_y(note->note());
1203 const double y2 = y1 + floor(midi_stream_view()->note_height());
1205 cnote->property_y1() = y1;
1206 cnote->property_y2() = y2;
1208 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1210 double x = trackview.editor().frame_to_pixel(
1211 beats_to_frames(note->time()) - _region->start());
1212 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1213 double y = midi_stream_view()->note_to_y(event->note()->note())
1214 + ((diamond_size-2.0) / 4.0);
1216 chit->set_height (diamond_size);
1217 chit->move (x - chit->x1(), y - chit->y1());
1224 MidiRegionView::add_ghost (TimeAxisView& tv)
1228 double unit_position = _region->position () / samples_per_unit;
1229 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1230 MidiGhostRegion* ghost;
1232 if (mtv && mtv->midi_view()) {
1233 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1234 to allow having midi notes on top of note lines and waveforms.
1236 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1238 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1241 ghost->set_height ();
1242 ghost->set_duration (_region->length() / samples_per_unit);
1243 ghosts.push_back (ghost);
1245 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1246 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1247 ghost->add_note(note);
1251 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1257 /** Begin tracking note state for successive calls to add_event
1260 MidiRegionView::begin_write()
1262 assert(!_active_notes);
1263 _active_notes = new CanvasNote*[128];
1264 for (unsigned i=0; i < 128; ++i) {
1265 _active_notes[i] = 0;
1270 /** Destroy note state for add_event
1273 MidiRegionView::end_write()
1275 delete[] _active_notes;
1277 _marked_for_selection.clear();
1278 _marked_for_velocity.clear();
1282 /** Resolve an active MIDI note (while recording).
1285 MidiRegionView::resolve_note(uint8_t note, double end_time)
1287 if (midi_view()->note_mode() != Sustained) {
1291 if (_active_notes && _active_notes[note]) {
1292 const nframes64_t end_time_frames = beats_to_frames(end_time);
1293 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1294 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1295 _active_notes[note] = 0;
1300 /** Extend active notes to rightmost edge of region (if length is changed)
1303 MidiRegionView::extend_active_notes()
1305 if (!_active_notes) {
1309 for (unsigned i=0; i < 128; ++i) {
1310 if (_active_notes[i]) {
1311 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1318 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1320 if (no_sound_notes || !trackview.editor().sound_notes()) {
1324 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1326 if (!route_ui || !route_ui->midi_track()) {
1330 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1336 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1338 if (no_sound_notes || !trackview.editor().sound_notes()) {
1342 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1344 if (!route_ui || !route_ui->midi_track()) {
1348 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1350 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1359 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1361 const nframes64_t note_start_frames = beats_to_frames(note->time());
1363 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1364 (note_start_frames < _region->start());
1366 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1367 (note->note() <= midi_stream_view()->highest_note());
1373 MidiRegionView::update_note (CanvasNote* ev)
1375 boost::shared_ptr<NoteType> note = ev->note();
1377 const nframes64_t note_start_frames = beats_to_frames(note->time());
1379 /* trim note display to not overlap the end of its region */
1380 const nframes64_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1382 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1383 const double y1 = midi_stream_view()->note_to_y(note->note());
1384 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1386 ev->property_x1() = x;
1387 ev->property_y1() = y1;
1388 if (note->length() > 0) {
1389 ev->property_x2() = note_endpixel;
1391 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1393 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1395 if (note->length() == 0) {
1396 if (_active_notes) {
1397 assert(note->note() < 128);
1398 // If this note is already active there's a stuck note,
1399 // finish the old note rectangle
1400 if (_active_notes[note->note()]) {
1401 CanvasNote* const old_rect = _active_notes[note->note()];
1402 boost::shared_ptr<NoteType> old_note = old_rect->note();
1403 old_rect->property_x2() = x;
1404 old_rect->property_outline_what() = (guint32) 0xF;
1406 _active_notes[note->note()] = ev;
1408 /* outline all but right edge */
1409 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1411 /* outline all edges */
1412 ev->property_outline_what() = (guint32) 0xF;
1417 MidiRegionView::update_hit (CanvasHit* ev)
1419 boost::shared_ptr<NoteType> note = ev->note();
1421 const nframes64_t note_start_frames = beats_to_frames(note->time());
1422 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1423 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1424 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1429 /** Add a MIDI note to the view (with length).
1431 * If in sustained mode, notes with length 0 will be considered active
1432 * notes, and resolve_note should be called when the corresponding note off
1433 * event arrives, to properly display the note.
1436 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1438 CanvasNoteEvent* event = 0;
1440 assert(note->time() >= 0);
1441 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1443 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1445 if (midi_view()->note_mode() == Sustained) {
1447 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1449 update_note (ev_rect);
1453 MidiGhostRegion* gr;
1455 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1456 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1457 gr->add_note(ev_rect);
1461 } else if (midi_view()->note_mode() == Percussive) {
1463 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1465 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1467 update_hit (ev_diamond);
1476 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1477 note_selected(event, true);
1480 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1481 event->show_velocity();
1483 event->on_channel_selection_change(_last_channel_selection);
1484 _events.push_back(event);
1495 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1496 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1498 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1500 /* potentially extend region to hold new note */
1502 nframes64_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1503 nframes64_t region_end = _region->position() + _region->length() - 1;
1505 if (end_frame > region_end) {
1506 _region->set_length (end_frame - _region->position(), this);
1509 _marked_for_selection.clear ();
1512 start_diff_command (_("step add"));
1513 diff_add_note (new_note, true, false);
1516 // last_step_edit_note = new_note;
1520 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1522 change_note_lengths (false, false, beats, false, true);
1526 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1528 assert(program.time >= 0);
1530 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1531 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1533 double height = midi_stream_view()->contents_height();
1535 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1536 new CanvasProgramChange(*this, *group,
1541 _custom_device_mode,
1542 program.time, program.channel, program.value));
1544 // Show unless program change is beyond the region bounds
1545 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1551 _pgm_changes.push_back(pgm_change);
1555 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1557 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1558 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1560 if (msb_control != 0) {
1561 msb = int(msb_control->get_double(true, time));
1564 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1565 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1567 if (lsb_control != 0) {
1568 lsb = lsb_control->get_double(true, time);
1571 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1572 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1573 double program_number = -1.0;
1574 if (program_control != 0) {
1575 program_number = program_control->get_double(true, time);
1578 key.msb = (int) floor(msb + 0.5);
1579 key.lsb = (int) floor(lsb + 0.5);
1580 key.program_number = (int) floor(program_number + 0.5);
1581 assert(key.is_sane());
1586 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1588 // TODO: Get the real event here and alter them at the original times
1589 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1590 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1591 if (msb_control != 0) {
1592 msb_control->set_double(double(new_patch.msb), true, old_program.time);
1595 // TODO: Get the real event here and alter them at the original times
1596 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1597 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1598 if (lsb_control != 0) {
1599 lsb_control->set_double(double(new_patch.lsb), true, old_program.time);
1602 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1603 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1605 assert(program_control != 0);
1606 program_control->set_double(float(new_patch.program_number), true, old_program.time);
1608 _pgm_changes.clear ();
1609 display_program_changes (); // XXX would be nice to limit to just old_program.channel
1613 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1615 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1616 alter_program_change(program_change_event, new_patch);
1620 MidiRegionView::previous_program(CanvasProgramChange& program)
1622 if (program.program() < 127) {
1623 MIDI::Name::PatchPrimaryKey key;
1624 get_patch_key_at(program.event_time(), program.channel(), key);
1625 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1627 key.program_number++;
1628 alter_program_change(program_change_event, key);
1633 MidiRegionView::next_program(CanvasProgramChange& program)
1635 if (program.program() > 0) {
1636 MIDI::Name::PatchPrimaryKey key;
1637 get_patch_key_at(program.event_time(), program.channel(), key);
1638 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1640 key.program_number--;
1641 alter_program_change(program_change_event, key);
1646 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1648 if (_selection.empty()) {
1652 if (_selection.erase (cne) > 0) {
1653 cerr << "Erased a CNE from selection\n";
1658 MidiRegionView::delete_selection()
1660 if (_selection.empty()) {
1664 start_diff_command (_("delete selection"));
1666 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1667 if ((*i)->selected()) {
1668 _diff_command->remove((*i)->note());
1678 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1680 start_diff_command (_("delete note"));
1681 _diff_command->remove (n);
1684 trackview.editor().hide_verbose_canvas_cursor ();
1688 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1690 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1691 if ((*i)->selected() && (*i) != ev) {
1692 (*i)->set_selected(false);
1693 (*i)->hide_velocity();
1698 _earliest_selected_time = Evoral::MaxMusicalTime;
1702 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1704 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1707 Selection::iterator tmp = i;
1710 (*i)->set_selected (false);
1711 _selection.erase (i);
1720 /* don't bother with removing this regionview from the editor selection,
1721 since we're about to add another note, and thus put/keep this
1722 regionview in the editor selection.
1725 if (!ev->selected()) {
1726 add_to_selection (ev);
1731 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1733 uint8_t low_note = 127;
1734 uint8_t high_note = 0;
1735 MidiModel::Notes& notes (_model->notes());
1736 _optimization_iterator = _events.begin();
1742 if (extend && _selection.empty()) {
1748 /* scan existing selection to get note range */
1750 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1751 if ((*i)->note()->note() < low_note) {
1752 low_note = (*i)->note()->note();
1754 if ((*i)->note()->note() > high_note) {
1755 high_note = (*i)->note()->note();
1759 low_note = min (low_note, notenum);
1760 high_note = max (high_note, notenum);
1763 no_sound_notes = true;
1765 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1767 boost::shared_ptr<NoteType> note (*n);
1768 CanvasNoteEvent* cne;
1769 bool select = false;
1771 if (((1 << note->channel()) & channel_mask) != 0) {
1773 if ((note->note() >= low_note && note->note() <= high_note)) {
1776 } else if (note->note() == notenum) {
1782 if ((cne = find_canvas_note (note)) != 0) {
1783 // extend is false because we've taken care of it,
1784 // since it extends by time range, not pitch.
1785 note_selected (cne, add, false);
1789 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1793 no_sound_notes = false;
1797 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1799 MidiModel::Notes& notes (_model->notes());
1800 _optimization_iterator = _events.begin();
1802 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1804 boost::shared_ptr<NoteType> note (*n);
1805 CanvasNoteEvent* cne;
1807 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1808 if ((cne = find_canvas_note (note)) != 0) {
1809 if (cne->selected()) {
1810 note_deselected (cne);
1812 note_selected (cne, true, false);
1820 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1823 clear_selection_except(ev);
1828 if (!ev->selected()) {
1829 add_to_selection (ev);
1833 /* find end of latest note selected, select all between that and the start of "ev" */
1835 Evoral::MusicalTime earliest = DBL_MAX;
1836 Evoral::MusicalTime latest = 0;
1838 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1839 if ((*i)->note()->end_time() > latest) {
1840 latest = (*i)->note()->end_time();
1842 if ((*i)->note()->time() < earliest) {
1843 earliest = (*i)->note()->time();
1847 if (ev->note()->end_time() > latest) {
1848 latest = ev->note()->end_time();
1851 if (ev->note()->time() < earliest) {
1852 earliest = ev->note()->time();
1855 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1857 /* find notes entirely within OR spanning the earliest..latest range */
1859 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1860 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1861 add_to_selection (*i);
1865 /* if events were guaranteed to be time sorted, we could do this.
1866 but as of sept 10th 2009, they no longer are.
1869 if ((*i)->note()->time() > latest) {
1878 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1880 remove_from_selection (ev);
1884 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1894 // TODO: Make this faster by storing the last updated selection rect, and only
1895 // adjusting things that are in the area that appears/disappeared.
1896 // We probably need a tree to be able to find events in O(log(n)) time.
1898 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1900 /* check if any corner of the note is inside the rect
1903 1) this is computing "touched by", not "contained by" the rect.
1904 2) this does not require that events be sorted in time.
1907 const double ix1 = (*i)->x1();
1908 const double ix2 = (*i)->x2();
1909 const double iy1 = (*i)->y1();
1910 const double iy2 = (*i)->y2();
1912 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1913 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1914 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1915 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1918 if (!(*i)->selected()) {
1919 add_to_selection (*i);
1921 } else if ((*i)->selected()) {
1922 // Not inside rectangle
1923 remove_from_selection (*i);
1929 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1931 Selection::iterator i = _selection.find (ev);
1933 if (i != _selection.end()) {
1934 _selection.erase (i);
1937 ev->set_selected (false);
1938 ev->hide_velocity ();
1941 if (Evoral::musical_time_equal (ev->note()->time(), _earliest_selected_time)) {
1943 _earliest_selected_time = Evoral::MaxMusicalTime;
1945 /* compute new earliest time */
1947 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1948 if (!Evoral::musical_time_equal ((*i)->note()->time(), _earliest_selected_time) &&
1949 (*i)->note()->time() < _earliest_selected_time) {
1950 _earliest_selected_time = (*i)->note()->time();
1955 if (_selection.empty()) {
1956 PublicEditor& editor (trackview.editor());
1957 editor.get_selection().remove (this);
1958 _earliest_selected_time = Evoral::MaxMusicalTime;
1963 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
1965 bool add_mrv_selection = false;
1967 if (_selection.empty()) {
1968 add_mrv_selection = true;
1971 if (_selection.insert (ev).second) {
1972 ev->set_selected (true);
1973 play_midi_note ((ev)->note());
1975 if (ev->note()->time() < _earliest_selected_time) {
1976 _earliest_selected_time = ev->note()->time();
1980 if (add_mrv_selection) {
1981 PublicEditor& editor (trackview.editor());
1982 editor.get_selection().add (this);
1987 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
1989 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
1990 PossibleChord to_play;
1992 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1993 if (Evoral::musical_time_equal ((*i)->note()->time(), _earliest_selected_time)) {
1994 to_play.push_back ((*i)->note());
1996 (*i)->move_event(dx, dy);
1999 if (dy && !_selection.empty() && !no_sound_notes && trackview.editor().sound_notes()) {
2001 if (to_play.size() > 1) {
2003 PossibleChord shifted;
2005 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2006 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2007 moved_note->set_note (moved_note->note() + cumulative_dy);
2008 shifted.push_back (moved_note);
2011 play_midi_chord (shifted);
2013 } else if (!to_play.empty()) {
2015 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2016 moved_note->set_note (moved_note->note() + cumulative_dy);
2017 play_midi_note (moved_note);
2023 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2025 assert (!_selection.empty());
2027 uint8_t lowest_note_in_selection = 127;
2028 uint8_t highest_note_in_selection = 0;
2029 uint8_t highest_note_difference = 0;
2031 // find highest and lowest notes first
2033 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2034 uint8_t pitch = (*i)->note()->note();
2035 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2036 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2040 cerr << "dnote: " << (int) dnote << endl;
2041 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2042 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2043 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2044 << int(highest_note_in_selection) << endl;
2045 cerr << "selection size: " << _selection.size() << endl;
2046 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2049 // Make sure the note pitch does not exceed the MIDI standard range
2050 if (highest_note_in_selection + dnote > 127) {
2051 highest_note_difference = highest_note_in_selection - 127;
2054 start_diff_command(_("move notes"));
2056 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2058 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2064 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
2066 uint8_t original_pitch = (*i)->note()->note();
2067 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2069 // keep notes in standard midi range
2070 clamp_to_0_127(new_pitch);
2072 // keep original pitch if note is dragged outside valid midi range
2073 if ((original_pitch != 0 && new_pitch == 0)
2074 || (original_pitch != 127 && new_pitch == 127)) {
2075 new_pitch = original_pitch;
2078 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2079 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2081 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
2086 // care about notes being moved beyond the upper/lower bounds on the canvas
2087 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2088 highest_note_in_selection > midi_stream_view()->highest_note()) {
2089 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2094 MidiRegionView::snap_pixel_to_frame(double x)
2096 PublicEditor& editor = trackview.editor();
2097 // x is region relative, convert it to global absolute frames
2098 nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
2099 editor.snap_to(frame);
2100 return frame - _region->position(); // convert back to region relative
2104 MidiRegionView::snap_frame_to_frame(nframes64_t x)
2106 PublicEditor& editor = trackview.editor();
2107 // x is region relative, convert it to global absolute frames
2108 nframes64_t frame = x + _region->position();
2109 editor.snap_to(frame);
2110 return frame - _region->position(); // convert back to region relative
2114 MidiRegionView::snap_to_pixel(double x)
2116 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2120 MidiRegionView::get_position_pixels()
2122 nframes64_t region_frame = get_position();
2123 return trackview.editor().frame_to_pixel(region_frame);
2127 MidiRegionView::get_end_position_pixels()
2129 nframes64_t frame = get_position() + get_duration ();
2130 return trackview.editor().frame_to_pixel(frame);
2134 MidiRegionView::beats_to_frames(double beats) const
2136 return _time_converter.to(beats);
2140 MidiRegionView::frames_to_beats(nframes64_t frames) const
2142 return _time_converter.from(frames);
2146 MidiRegionView::begin_resizing (bool /*at_front*/)
2148 _resize_data.clear();
2150 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2151 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2153 // only insert CanvasNotes into the map
2155 NoteResizeData *resize_data = new NoteResizeData();
2156 resize_data->canvas_note = note;
2158 // create a new SimpleRect from the note which will be the resize preview
2159 SimpleRect *resize_rect = new SimpleRect(
2160 *group, note->x1(), note->y1(), note->x2(), note->y2());
2162 // calculate the colors: get the color settings
2163 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2164 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2167 // make the resize preview notes more transparent and bright
2168 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2170 // calculate color based on note velocity
2171 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2172 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2176 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2177 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2179 resize_data->resize_rect = resize_rect;
2180 _resize_data.push_back(resize_data);
2185 /** Update resizing notes while user drags.
2186 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2187 * @param at_front which end of the note (true == note on, false == note off)
2188 * @param delta_x change in mouse position since the start of the drag
2189 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2190 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2191 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2192 * as the \a primary note.
2195 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2197 bool cursor_set = false;
2199 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2200 SimpleRect* resize_rect = (*i)->resize_rect;
2201 CanvasNote* canvas_note = (*i)->canvas_note;
2206 current_x = canvas_note->x1() + delta_x;
2208 current_x = primary->x1() + delta_x;
2212 current_x = canvas_note->x2() + delta_x;
2214 current_x = primary->x2() + delta_x;
2219 resize_rect->property_x1() = snap_to_pixel(current_x);
2220 resize_rect->property_x2() = canvas_note->x2();
2222 resize_rect->property_x2() = snap_to_pixel(current_x);
2223 resize_rect->property_x1() = canvas_note->x1();
2229 beats = snap_pixel_to_frame (current_x);
2230 beats = frames_to_beats (beats);
2235 if (beats < canvas_note->note()->end_time()) {
2236 len = canvas_note->note()->time() - beats;
2237 len += canvas_note->note()->length();
2242 if (beats >= canvas_note->note()->end_time()) {
2243 len = beats - canvas_note->note()->time();
2250 snprintf (buf, sizeof (buf), "%.3g beats", len);
2251 trackview.editor().show_verbose_canvas_cursor_with (buf);
2260 /** Finish resizing notes when the user releases the mouse button.
2261 * Parameters the same as for \a update_resizing().
2264 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2266 start_diff_command(_("resize notes"));
2268 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2269 CanvasNote* canvas_note = (*i)->canvas_note;
2270 SimpleRect* resize_rect = (*i)->resize_rect;
2275 current_x = canvas_note->x1() + delta_x;
2277 current_x = primary->x1() + delta_x;
2281 current_x = canvas_note->x2() + delta_x;
2283 current_x = primary->x2() + delta_x;
2287 current_x = snap_pixel_to_frame (current_x);
2288 current_x = frames_to_beats (current_x);
2290 if (at_front && current_x < canvas_note->note()->end_time()) {
2291 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2293 double len = canvas_note->note()->time() - current_x;
2294 len += canvas_note->note()->length();
2297 /* XXX convert to beats */
2298 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2303 double len = current_x - canvas_note->note()->time();
2306 /* XXX convert to beats */
2307 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2315 _resize_data.clear();
2320 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2322 diff_add_change (event, MidiModel::DiffCommand::Channel, (uint8_t) channel);
2326 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2328 uint8_t new_velocity;
2331 new_velocity = event->note()->velocity() + velocity;
2332 clamp_to_0_127(new_velocity);
2334 new_velocity = velocity;
2337 event->set_selected (event->selected()); // change color
2339 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2343 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2348 new_note = event->note()->note() + note;
2353 clamp_to_0_127 (new_note);
2354 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2358 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2360 bool change_start = false;
2361 bool change_length = false;
2362 Evoral::MusicalTime new_start = 0;
2363 Evoral::MusicalTime new_length = 0;
2365 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2367 front_delta: if positive - move the start of the note later in time (shortening it)
2368 if negative - move the start of the note earlier in time (lengthening it)
2370 end_delta: if positive - move the end of the note later in time (lengthening it)
2371 if negative - move the end of the note earlier in time (shortening it)
2375 if (front_delta < 0) {
2377 if (event->note()->time() < -front_delta) {
2380 new_start = event->note()->time() + front_delta; // moves earlier
2383 /* start moved toward zero, so move the end point out to where it used to be.
2384 Note that front_delta is negative, so this increases the length.
2387 new_length = event->note()->length() - front_delta;
2388 change_start = true;
2389 change_length = true;
2393 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2395 if (new_pos < event->note()->end_time()) {
2396 new_start = event->note()->time() + front_delta;
2397 /* start moved toward the end, so move the end point back to where it used to be */
2398 new_length = event->note()->length() - front_delta;
2399 change_start = true;
2400 change_length = true;
2407 bool can_change = true;
2408 if (end_delta < 0) {
2409 if (event->note()->length() < -end_delta) {
2415 new_length = event->note()->length() + end_delta;
2416 change_length = true;
2421 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2424 if (change_length) {
2425 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2430 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2432 Evoral::MusicalTime new_time;
2436 if (event->note()->time() < -delta) {
2439 new_time = event->note()->time() + delta;
2442 new_time = event->note()->time() + delta;
2448 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2452 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2454 diff_add_change (event, MidiModel::DiffCommand::Length, t);
2458 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2462 if (_selection.empty()) {
2477 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2478 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2484 start_diff_command(_("change velocities"));
2486 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2487 Selection::iterator next = i;
2489 change_note_velocity (*i, delta, true);
2495 if (!_selection.empty()) {
2497 snprintf (buf, sizeof (buf), "Vel %d",
2498 (int) (*_selection.begin())->note()->velocity());
2499 trackview.editor().show_verbose_canvas_cursor_with (buf);
2505 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2507 if (_selection.empty()) {
2524 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2526 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2530 if ((int8_t) (*i)->note()->note() + delta > 127) {
2537 start_diff_command (_("transpose"));
2539 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2540 Selection::iterator next = i;
2542 change_note_note (*i, delta, true);
2550 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2556 /* grab the current grid distance */
2558 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2560 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2561 cerr << "Grid type not available as beats - TO BE FIXED\n";
2571 start_diff_command (_("change note lengths"));
2573 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2574 Selection::iterator next = i;
2577 /* note the negation of the delta for start */
2579 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2588 MidiRegionView::nudge_notes (bool forward)
2590 if (_selection.empty()) {
2594 /* pick a note as the point along the timeline to get the nudge distance.
2595 its not necessarily the earliest note, so we may want to pull the notes out
2596 into a vector and sort before using the first one.
2599 nframes64_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2601 nframes64_t distance;
2603 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2605 /* grid is off - use nudge distance */
2607 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2613 nframes64_t next_pos = ref_point;
2616 /* XXX need check on max_frames, but that needs max_frames64 or something */
2619 if (next_pos == 0) {
2625 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2626 distance = ref_point - next_pos;
2629 if (distance == 0) {
2633 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2639 start_diff_command (_("nudge"));
2641 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2642 Selection::iterator next = i;
2644 change_note_time (*i, delta, true);
2652 MidiRegionView::change_channel(uint8_t channel)
2654 start_diff_command(_("change channel"));
2655 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2656 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2664 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2666 if (_mouse_state == SelectTouchDragging) {
2667 note_selected (ev, true);
2670 show_verbose_canvas_cursor (ev->note ());
2674 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent* note)
2676 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2677 (*i)->hide_velocity ();
2680 trackview.editor().hide_verbose_canvas_cursor ();
2684 MidiRegionView::switch_source(boost::shared_ptr<Source> src)
2686 boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(src);
2688 display_model(msrc->model());
2692 MidiRegionView::set_frame_color()
2695 if (_selected && should_show_selection) {
2696 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2698 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2704 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2708 case FilterChannels:
2709 _force_channel = -1;
2712 _force_channel = mask;
2713 mask = 0xFFFF; // Show all notes as active (below)
2716 // Update notes for selection
2717 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2718 (*i)->on_channel_selection_change(mask);
2721 _last_channel_selection = mask;
2725 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2727 _model_name = model;
2728 _custom_device_mode = custom_device_mode;
2733 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2735 if (_selection.empty()) {
2739 PublicEditor& editor (trackview.editor());
2744 editor.get_cut_buffer().add (selection_as_cut_buffer());
2752 start_diff_command();
2754 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2760 diff_remove_note (*i);
2770 MidiRegionView::selection_as_cut_buffer () const
2774 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2775 NoteType* n = (*i)->note().get();
2776 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2779 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2786 MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
2792 start_diff_command (_("paste"));
2794 Evoral::MusicalTime beat_delta;
2795 Evoral::MusicalTime paste_pos_beats;
2796 Evoral::MusicalTime duration;
2797 Evoral::MusicalTime end_point = 0;
2799 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2800 paste_pos_beats = frames_to_beats (pos - _region->position());
2801 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2802 paste_pos_beats = 0;
2806 for (int n = 0; n < (int) times; ++n) {
2808 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2810 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2811 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2813 /* make all newly added notes selected */
2815 diff_add_note (copied_note, true);
2816 end_point = copied_note->end_time();
2819 paste_pos_beats += duration;
2822 /* if we pasted past the current end of the region, extend the region */
2824 nframes64_t end_frame = _region->position() + beats_to_frames (end_point);
2825 nframes64_t region_end = _region->position() + _region->length() - 1;
2827 if (end_frame > region_end) {
2829 trackview.session()->begin_reversible_command (_("paste"));
2831 _region->clear_changes ();
2832 _region->set_length (end_frame, this);
2833 trackview.session()->add_command (new StatefulDiffCommand (_region));
2839 struct EventNoteTimeEarlyFirstComparator {
2840 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2841 return a->note()->time() < b->note()->time();
2846 MidiRegionView::time_sort_events ()
2848 if (!_sort_needed) {
2852 EventNoteTimeEarlyFirstComparator cmp;
2855 _sort_needed = false;
2859 MidiRegionView::goto_next_note ()
2861 // nframes64_t pos = -1;
2862 bool use_next = false;
2864 if (_events.back()->selected()) {
2868 time_sort_events ();
2870 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2871 if ((*i)->selected()) {
2874 } else if (use_next) {
2876 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2881 /* use the first one */
2883 unique_select (_events.front());
2888 MidiRegionView::goto_previous_note ()
2890 // nframes64_t pos = -1;
2891 bool use_next = false;
2893 if (_events.front()->selected()) {
2897 time_sort_events ();
2899 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2900 if ((*i)->selected()) {
2903 } else if (use_next) {
2905 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2910 /* use the last one */
2912 unique_select (*(_events.rbegin()));
2916 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2918 bool had_selected = false;
2920 time_sort_events ();
2922 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2923 if ((*i)->selected()) {
2924 selected.insert ((*i)->note());
2925 had_selected = true;
2929 if (allow_all_if_none_selected && !had_selected) {
2930 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2931 selected.insert ((*i)->note());
2937 MidiRegionView::update_ghost_note (double x, double y)
2943 nframes64_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
2944 trackview.editor().snap_to (f);
2945 f -= _region->position ();
2948 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
2953 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
2955 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
2956 _ghost_note->note()->set_length (length);
2957 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
2959 update_note (_ghost_note);
2961 show_verbose_canvas_cursor (_ghost_note->note ());
2965 MidiRegionView::create_ghost_note (double x, double y)
2970 boost::shared_ptr<NoteType> g (new NoteType);
2971 _ghost_note = new NoEventCanvasNote (*this, *group, g);
2972 update_ghost_note (x, y);
2973 _ghost_note->show ();
2978 show_verbose_canvas_cursor (_ghost_note->note ());
2982 MidiRegionView::snap_changed ()
2988 create_ghost_note (_last_ghost_x, _last_ghost_y);
2992 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
2995 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
2996 Evoral::midi_note_name (n->note()).c_str(),
2998 (int) n->velocity());
2999 trackview.editor().show_verbose_canvas_cursor_with (buf);
3003 MidiRegionView::drop_down_keys ()
3005 _mouse_state = None;
3009 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double x, double y)
3011 double note = midi_stream_view()->y_to_note(y);
3013 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3015 cerr << "Selecting by position\n";
3017 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3019 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3020 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3021 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3022 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3027 bool add_mrv_selection = false;
3029 if (_selection.empty()) {
3030 add_mrv_selection = true;
3033 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3034 if (_selection.insert (*i).second) {
3035 (*i)->set_selected (true);
3039 if (add_mrv_selection) {
3040 PublicEditor& editor (trackview.editor());
3041 editor.get_selection().add (this);
3046 MidiRegionView::color_handler ()
3048 RegionView::color_handler ();
3050 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3051 (*i)->set_selected ((*i)->selected()); // will change color
3054 /* XXX probably more to do here */
3058 MidiRegionView::enable_display (bool yn)
3060 RegionView::enable_display (yn);
3067 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3069 if (_step_edit_cursor == 0) {
3070 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3072 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3073 _step_edit_cursor->property_y1() = 0;
3074 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3075 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3076 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3079 move_step_edit_cursor (pos);
3080 _step_edit_cursor->show ();
3084 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3086 _step_edit_cursor_position = pos;
3088 if (_step_edit_cursor) {
3089 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3090 _step_edit_cursor->property_x1() = pixel;
3091 set_step_edit_cursor_width (_step_edit_cursor_width);
3096 MidiRegionView::hide_step_edit_cursor ()
3098 if (_step_edit_cursor) {
3099 _step_edit_cursor->hide ();
3104 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3106 _step_edit_cursor_width = beats;
3108 if (_step_edit_cursor) {
3109 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));