2 Copyright (C) 2001-2007 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <gtkmm2ext/gtk_ui.h>
29 #include <sigc++/signal.h>
31 #include "pbd/memento_command.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "ardour/playlist.h"
35 #include "ardour/tempo.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_model.h"
39 #include "ardour/midi_patch_manager.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIParameters.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "automation_region_view.h"
48 #include "automation_time_axis.h"
49 #include "canvas-hit.h"
50 #include "canvas-note.h"
51 #include "canvas-program-change.h"
53 #include "ghostregion.h"
54 #include "gui_thread.h"
56 #include "midi_cut_buffer.h"
57 #include "midi_list_editor.h"
58 #include "midi_region_view.h"
59 #include "midi_streamview.h"
60 #include "midi_time_axis.h"
61 #include "midi_time_axis.h"
62 #include "midi_util.h"
63 #include "note_player.h"
64 #include "public_editor.h"
65 #include "rgb_macros.h"
66 #include "selection.h"
67 #include "simpleline.h"
68 #include "streamview.h"
73 using namespace ARDOUR;
75 using namespace Editing;
76 using namespace ArdourCanvas;
77 using Gtkmm2ext::Keyboard;
79 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
80 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
81 : RegionView (parent, tv, r, spu, basic_color)
83 , _last_channel_selection(0xFFFF)
84 , _current_range_min(0)
85 , _current_range_max(0)
86 , _model_name(string())
87 , _custom_device_mode(string())
89 , _note_group(new ArdourCanvas::Group(*parent))
93 , _step_edit_cursor (0)
94 , _step_edit_cursor_width (1.0)
95 , _step_edit_cursor_position (0.0)
99 , _optimization_iterator (_events.end())
101 , no_sound_notes (false)
102 , pre_enter_cursor (0)
104 _note_group->raise_to_top();
105 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
108 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
109 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
110 TimeAxisViewItem::Visibility visibility)
111 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
113 , _last_channel_selection(0xFFFF)
114 , _model_name(string())
115 , _custom_device_mode(string())
117 , _note_group(new ArdourCanvas::Group(*parent))
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()))
146 , _sort_needed (true)
147 , _optimization_iterator (_events.end())
149 , no_sound_notes (false)
154 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
155 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
160 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
161 : RegionView (other, boost::shared_ptr<Region> (region))
163 , _last_channel_selection(0xFFFF)
164 , _model_name(string())
165 , _custom_device_mode(string())
167 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
173 , _sort_needed (true)
174 , _optimization_iterator (_events.end())
176 , no_sound_notes (false)
181 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
182 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
188 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
190 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
192 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
193 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
197 midi_region()->midi_source(0)->load_model();
200 _model = midi_region()->midi_source(0)->model();
201 _enable_display = false;
203 RegionView::init (basic_color, false);
205 compute_colors (basic_color);
207 set_height (trackview.current_height());
210 region_sync_changed ();
211 region_resized (ARDOUR::bounds_change);
214 reset_width_dependent_items (_pixel_width);
218 _enable_display = true;
221 display_model (_model);
225 group->raise_to_top();
226 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
228 midi_view()->signal_channel_mode_changed().connect(
229 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
231 midi_view()->signal_midi_patch_settings_changed().connect(
232 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
234 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
238 MidiRegionView::canvas_event(GdkEvent* ev)
240 if (!trackview.editor().internal_editing()) {
244 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
245 to its items, which means that ev->type == GDK_SCROLL will never be seen
250 return scroll (&ev->scroll);
253 return key_press (&ev->key);
255 case GDK_KEY_RELEASE:
256 return key_release (&ev->key);
258 case GDK_BUTTON_PRESS:
259 return button_press (&ev->button);
261 case GDK_2BUTTON_PRESS:
264 case GDK_BUTTON_RELEASE:
265 return button_release (&ev->button);
267 case GDK_ENTER_NOTIFY:
268 return enter_notify (&ev->crossing);
270 case GDK_LEAVE_NOTIFY:
271 return leave_notify (&ev->crossing);
273 case GDK_MOTION_NOTIFY:
274 return motion (&ev->motion);
284 MidiRegionView::enter_notify (GdkEventCrossing* ev)
286 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
288 Keyboard::magic_widget_grab_focus();
291 if (trackview.editor().current_mouse_mode() == MouseRange) {
292 create_ghost_note (ev->x, ev->y);
299 MidiRegionView::leave_notify (GdkEventCrossing* ev)
301 trackview.editor().hide_verbose_canvas_cursor ();
308 MidiRegionView::button_press (GdkEventButton* ev)
312 group->w2i (_last_x, _last_y);
314 if (_mouse_state != SelectTouchDragging && ev->button == 1) {
315 _pressed_button = ev->button;
316 _mouse_state = Pressed;
320 _pressed_button = ev->button;
326 MidiRegionView::button_release (GdkEventButton* ev)
328 double event_x, event_y;
329 framepos_t event_frame = 0;
333 group->w2i(event_x, event_y);
334 group->ungrab(ev->time);
335 event_frame = trackview.editor().pixel_to_frame(event_x);
337 if (ev->button == 3) {
339 } else if (_pressed_button != 1) {
343 switch (_mouse_state) {
344 case Pressed: // Clicked
345 switch (trackview.editor().current_mouse_mode()) {
349 maybe_select_by_position (ev, event_x, event_y);
355 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
359 create_note_at (event_x, event_y, beats, true);
367 case SelectRectDragging: // Select drag done
373 case AddDragging: // Add drag done
375 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
376 const double x = _drag_rect->property_x1();
377 const double length = trackview.editor().pixel_to_frame
378 (_drag_rect->property_x2() - _drag_rect->property_x1());
380 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), false);
386 create_ghost_note (ev->x, ev->y);
396 MidiRegionView::motion (GdkEventMotion* ev)
398 double event_x, event_y;
399 framepos_t event_frame = 0;
403 group->w2i(event_x, event_y);
405 // convert event_x to global frame
406 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
407 trackview.editor().snap_to(event_frame);
408 // convert event_frame back to local coordinates relative to position
409 event_frame -= _region->position();
412 update_ghost_note (ev->x, ev->y);
415 /* any motion immediately hides velocity text that may have been visible */
417 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
418 (*i)->hide_velocity ();
421 switch (_mouse_state) {
422 case Pressed: // Maybe start a drag, if we've moved a bit
424 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
425 /* no appreciable movement since the button was pressed */
430 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
431 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
432 Gdk::Cursor(Gdk::FLEUR), ev->time);
435 _drag_start_x = event_x;
436 _drag_start_y = event_y;
438 _drag_rect = new ArdourCanvas::SimpleRect(*group);
439 _drag_rect->property_x1() = event_x;
440 _drag_rect->property_y1() = event_y;
441 _drag_rect->property_x2() = event_x;
442 _drag_rect->property_y2() = event_y;
443 _drag_rect->property_outline_what() = 0xFF;
444 _drag_rect->property_outline_color_rgba()
445 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
446 _drag_rect->property_fill_color_rgba()
447 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
449 _mouse_state = SelectRectDragging;
452 // Add note drag start
453 } else if (trackview.editor().internal_editing()) {
458 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
459 Gdk::Cursor(Gdk::FLEUR), ev->time);
462 _drag_start_x = event_x;
463 _drag_start_y = event_y;
465 _drag_rect = new ArdourCanvas::SimpleRect(*group);
466 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
468 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
469 midi_stream_view()->y_to_note(event_y));
470 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
471 _drag_rect->property_y2() = _drag_rect->property_y1()
472 + floor(midi_stream_view()->note_height());
473 _drag_rect->property_outline_what() = 0xFF;
474 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
475 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
477 _mouse_state = AddDragging;
483 case SelectRectDragging: // Select drag motion
484 case AddDragging: // Add note drag motion
488 GdkModifierType state;
489 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
494 if (_mouse_state == AddDragging)
495 event_x = trackview.editor().frame_to_pixel(event_frame);
498 if (event_x > _drag_start_x)
499 _drag_rect->property_x2() = event_x;
501 _drag_rect->property_x1() = event_x;
504 if (_drag_rect && _mouse_state == SelectRectDragging) {
505 if (event_y > _drag_start_y)
506 _drag_rect->property_y2() = event_y;
508 _drag_rect->property_y1() = event_y;
510 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
516 case SelectTouchDragging:
528 MidiRegionView::scroll (GdkEventScroll* ev)
530 if (_selection.empty()) {
534 trackview.editor().hide_verbose_canvas_cursor ();
536 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
538 if (ev->direction == GDK_SCROLL_UP) {
539 change_velocities (true, fine, false);
540 } else if (ev->direction == GDK_SCROLL_DOWN) {
541 change_velocities (false, fine, false);
547 MidiRegionView::key_press (GdkEventKey* ev)
549 /* since GTK bindings are generally activated on press, and since
550 detectable auto-repeat is the name of the game and only sends
551 repeated presses, carry out key actions at key press, not release.
554 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
555 _mouse_state = SelectTouchDragging;
558 } else if (ev->keyval == GDK_Escape) {
562 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
564 bool start = (ev->keyval == GDK_comma);
565 bool end = (ev->keyval == GDK_period);
566 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
567 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
569 change_note_lengths (fine, shorter, 0.0, start, end);
573 } else if (ev->keyval == GDK_Delete) {
578 } else if (ev->keyval == GDK_Tab) {
580 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
581 goto_previous_note ();
587 } else if (ev->keyval == GDK_Up) {
589 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
590 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
592 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
593 change_velocities (true, fine, allow_smush);
595 transpose (true, fine, allow_smush);
599 } else if (ev->keyval == GDK_Down) {
601 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
602 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
604 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
605 change_velocities (false, fine, allow_smush);
607 transpose (false, fine, allow_smush);
611 } else if (ev->keyval == GDK_Left) {
616 } else if (ev->keyval == GDK_Right) {
621 } else if (ev->keyval == GDK_Control_L) {
630 MidiRegionView::key_release (GdkEventKey* ev)
632 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
640 MidiRegionView::show_list_editor ()
643 _list_editor = new MidiListEditor (trackview.session(), midi_region());
645 _list_editor->present ();
648 /** Add a note to the model, and the view, at a canvas (click) coordinate.
649 * \param x horizontal position in pixels
650 * \param y vertical position in pixels
651 * \param length duration of the note in beats, which will be snapped to the grid
652 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
655 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
657 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
658 MidiStreamView* const view = mtv->midi_view();
660 double note = midi_stream_view()->y_to_note(y);
663 assert(note <= 127.0);
665 // Start of note in frames relative to region start
666 framepos_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
667 assert(start_frames >= 0);
670 length = frames_to_beats(
671 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
673 assert (length != 0);
676 length = frames_to_beats (beats_to_frames (length) - 1);
679 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
683 /* pick the highest selected channel, unless all channels are selected,
684 which is interpreted to mean channel 1 (zero)
687 for (uint16_t i = 0; i < 16; ++i) {
688 if (chn_mask & (1<<i)) {
698 const boost::shared_ptr<NoteType> new_note (new NoteType (channel,
699 frames_to_beats(start_frames + _region->start()), length,
700 (uint8_t)note, 0x40));
702 if (_model->contains (new_note)) {
706 view->update_note_range(new_note->note());
708 MidiModel::DiffCommand* cmd = _model->new_diff_command("add note");
710 _model->apply_command(*trackview.session(), cmd);
712 play_midi_note (new_note);
716 MidiRegionView::clear_events()
721 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
722 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
727 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
732 _pgm_changes.clear();
734 _optimization_iterator = _events.end();
739 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
742 content_connection.disconnect ();
743 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
747 if (_enable_display) {
753 MidiRegionView::start_diff_command(string name)
755 if (!_diff_command) {
756 _diff_command = _model->new_diff_command(name);
761 MidiRegionView::diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
764 _diff_command->add(note);
767 _marked_for_selection.insert(note);
770 _marked_for_velocity.insert(note);
775 MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
777 if (_diff_command && ev->note()) {
778 _diff_command->remove(ev->note());
783 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
784 MidiModel::DiffCommand::Property property,
788 _diff_command->change (ev->note(), property, val);
793 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
794 MidiModel::DiffCommand::Property property,
795 Evoral::MusicalTime val)
798 _diff_command->change (ev->note(), property, val);
803 MidiRegionView::apply_diff ()
807 if (!_diff_command) {
811 if ((add_or_remove = _diff_command->adds_or_removes())) {
812 // Mark all selected notes for selection when model reloads
813 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
814 _marked_for_selection.insert((*i)->note());
818 _model->apply_command(*trackview.session(), _diff_command);
820 midi_view()->midi_track()->playlist_modified();
823 _marked_for_selection.clear();
826 _marked_for_velocity.clear();
830 MidiRegionView::apply_diff_as_subcommand()
834 if (!_diff_command) {
838 if ((add_or_remove = _diff_command->adds_or_removes())) {
839 // Mark all selected notes for selection when model reloads
840 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
841 _marked_for_selection.insert((*i)->note());
845 _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
847 midi_view()->midi_track()->playlist_modified();
850 _marked_for_selection.clear();
852 _marked_for_velocity.clear();
857 MidiRegionView::abort_command()
859 delete _diff_command;
865 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
867 if (_optimization_iterator != _events.end()) {
868 ++_optimization_iterator;
871 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
872 return *_optimization_iterator;
875 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
876 if ((*_optimization_iterator)->note() == note) {
877 return *_optimization_iterator;
885 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
887 MidiModel::Notes notes;
888 _model->get_notes (notes, op, val, chan_mask);
890 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
891 CanvasNoteEvent* cne = find_canvas_note (*n);
899 MidiRegionView::redisplay_model()
901 // Don't redisplay the model if we're currently recording and displaying that
907 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
911 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
915 MidiModel::ReadLock lock(_model->read_lock());
917 MidiModel::Notes& notes (_model->notes());
918 _optimization_iterator = _events.begin();
920 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
922 boost::shared_ptr<NoteType> note (*n);
923 CanvasNoteEvent* cne;
926 if (note_in_region_range (note, visible)) {
928 if ((cne = find_canvas_note (note)) != 0) {
935 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
937 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
949 add_note (note, visible);
954 if ((cne = find_canvas_note (note)) != 0) {
962 /* remove note items that are no longer valid */
964 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
965 if (!(*i)->valid ()) {
967 i = _events.erase (i);
973 _pgm_changes.clear();
977 display_program_changes();
979 _marked_for_selection.clear ();
980 _marked_for_velocity.clear ();
982 /* we may have caused _events to contain things out of order (e.g. if a note
983 moved earlier or later). we don't generally need them in time order, but
984 make a note that a sort is required for those cases that require it.
991 MidiRegionView::display_program_changes()
993 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
994 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
996 for (uint8_t i = 0; i < 16; ++i) {
997 if (chn_mask & (1<<i)) {
998 display_program_changes_on_channel (i);
1004 MidiRegionView::display_program_changes_on_channel(uint8_t channel)
1006 boost::shared_ptr<Evoral::Control> control =
1007 _model->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation, channel));
1013 Glib::Mutex::Lock lock (control->list()->lock());
1015 for (AutomationList::const_iterator event = control->list()->begin();
1016 event != control->list()->end(); ++event) {
1017 double event_time = (*event)->when;
1018 double program_number = floor((*event)->value + 0.5);
1020 // Get current value of bank select MSB at time of the program change
1021 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1022 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1024 if (msb_control != 0) {
1025 msb = uint8_t(floor(msb_control->get_double(true, event_time) + 0.5));
1028 // Get current value of bank select LSB at time of the program change
1029 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1030 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1032 if (lsb_control != 0) {
1033 lsb = uint8_t(floor(lsb_control->get_double(true, event_time) + 0.5));
1036 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
1038 boost::shared_ptr<MIDI::Name::Patch> patch =
1039 MIDI::Name::MidiPatchManager::instance().find_patch(
1040 _model_name, _custom_device_mode, channel, patch_key);
1042 PCEvent program_change(event_time, uint8_t(program_number), channel);
1045 add_pgm_change(program_change, patch->name());
1048 // program_number is zero-based: convert to one-based
1049 snprintf(buf, 4, "%d", int(program_number+1));
1050 add_pgm_change(program_change, buf);
1056 MidiRegionView::display_sysexes()
1058 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1059 Evoral::MusicalTime time = (*i)->time();
1064 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1065 str << int((*i)->buffer()[b]);
1066 if (b != (*i)->size() -1) {
1070 string text = str.str();
1072 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1074 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1076 double height = midi_stream_view()->contents_height();
1078 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1079 new CanvasSysEx(*this, *group, text, height, x, 1.0));
1081 // Show unless program change is beyond the region bounds
1082 if (time - _region->start() >= _region->length() || time < _region->start()) {
1088 _sys_exes.push_back(sysex);
1093 MidiRegionView::~MidiRegionView ()
1095 in_destructor = true;
1097 trackview.editor().hide_verbose_canvas_cursor ();
1099 note_delete_connection.disconnect ();
1101 delete _list_editor;
1103 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1105 if (_active_notes) {
1113 delete _diff_command;
1114 delete _step_edit_cursor;
1118 MidiRegionView::region_resized (const PropertyChange& what_changed)
1120 RegionView::region_resized(what_changed);
1122 if (what_changed.contains (ARDOUR::Properties::position)) {
1123 set_duration(_region->length(), 0);
1124 if (_enable_display) {
1131 MidiRegionView::reset_width_dependent_items (double pixel_width)
1133 RegionView::reset_width_dependent_items(pixel_width);
1134 assert(_pixel_width == pixel_width);
1136 if (_enable_display) {
1140 move_step_edit_cursor (_step_edit_cursor_position);
1141 set_step_edit_cursor_width (_step_edit_cursor_width);
1145 MidiRegionView::set_height (double height)
1147 static const double FUDGE = 2.0;
1148 const double old_height = _height;
1149 RegionView::set_height(height);
1150 _height = height - FUDGE;
1152 apply_note_range(midi_stream_view()->lowest_note(),
1153 midi_stream_view()->highest_note(),
1154 height != old_height + FUDGE);
1157 name_pixbuf->raise_to_top();
1160 for (PgmChanges::iterator x = _pgm_changes.begin(); x != _pgm_changes.end(); ++x) {
1161 (*x)->set_height (midi_stream_view()->contents_height());
1164 if (_step_edit_cursor) {
1165 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1170 /** Apply the current note range from the stream view
1171 * by repositioning/hiding notes as necessary
1174 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1176 if (!_enable_display) {
1180 if (!force && _current_range_min == min && _current_range_max == max) {
1184 _current_range_min = min;
1185 _current_range_max = max;
1187 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1188 CanvasNoteEvent* event = *i;
1189 boost::shared_ptr<NoteType> note (event->note());
1191 if (note->note() < _current_range_min ||
1192 note->note() > _current_range_max) {
1198 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1200 const double y1 = midi_stream_view()->note_to_y(note->note());
1201 const double y2 = y1 + floor(midi_stream_view()->note_height());
1203 cnote->property_y1() = y1;
1204 cnote->property_y2() = y2;
1206 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1208 double x = trackview.editor().frame_to_pixel(
1209 beats_to_frames(note->time()) - _region->start());
1210 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1211 double y = midi_stream_view()->note_to_y(event->note()->note())
1212 + ((diamond_size-2.0) / 4.0);
1214 chit->set_height (diamond_size);
1215 chit->move (x - chit->x1(), y - chit->y1());
1222 MidiRegionView::add_ghost (TimeAxisView& tv)
1226 double unit_position = _region->position () / samples_per_unit;
1227 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1228 MidiGhostRegion* ghost;
1230 if (mtv && mtv->midi_view()) {
1231 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1232 to allow having midi notes on top of note lines and waveforms.
1234 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1236 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1239 ghost->set_height ();
1240 ghost->set_duration (_region->length() / samples_per_unit);
1241 ghosts.push_back (ghost);
1243 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1244 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1245 ghost->add_note(note);
1249 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1255 /** Begin tracking note state for successive calls to add_event
1258 MidiRegionView::begin_write()
1260 assert(!_active_notes);
1261 _active_notes = new CanvasNote*[128];
1262 for (unsigned i=0; i < 128; ++i) {
1263 _active_notes[i] = 0;
1268 /** Destroy note state for add_event
1271 MidiRegionView::end_write()
1273 delete[] _active_notes;
1275 _marked_for_selection.clear();
1276 _marked_for_velocity.clear();
1280 /** Resolve an active MIDI note (while recording).
1283 MidiRegionView::resolve_note(uint8_t note, double end_time)
1285 if (midi_view()->note_mode() != Sustained) {
1289 if (_active_notes && _active_notes[note]) {
1290 const framepos_t end_time_frames = beats_to_frames(end_time);
1291 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1292 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1293 _active_notes[note] = 0;
1298 /** Extend active notes to rightmost edge of region (if length is changed)
1301 MidiRegionView::extend_active_notes()
1303 if (!_active_notes) {
1307 for (unsigned i=0; i < 128; ++i) {
1308 if (_active_notes[i]) {
1309 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1316 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1318 if (no_sound_notes || !trackview.editor().sound_notes()) {
1322 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1324 if (!route_ui || !route_ui->midi_track()) {
1328 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1334 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1336 if (no_sound_notes || !trackview.editor().sound_notes()) {
1340 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1342 if (!route_ui || !route_ui->midi_track()) {
1346 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1348 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1357 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1359 const framepos_t note_start_frames = beats_to_frames(note->time());
1361 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1362 (note_start_frames < _region->start());
1364 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1365 (note->note() <= midi_stream_view()->highest_note());
1371 MidiRegionView::update_note (CanvasNote* ev)
1373 boost::shared_ptr<NoteType> note = ev->note();
1375 const framepos_t note_start_frames = beats_to_frames(note->time());
1377 /* trim note display to not overlap the end of its region */
1378 const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1380 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1381 const double y1 = midi_stream_view()->note_to_y(note->note());
1382 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1384 ev->property_x1() = x;
1385 ev->property_y1() = y1;
1386 if (note->length() > 0) {
1387 ev->property_x2() = note_endpixel;
1389 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1391 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1393 if (note->length() == 0) {
1394 if (_active_notes) {
1395 assert(note->note() < 128);
1396 // If this note is already active there's a stuck note,
1397 // finish the old note rectangle
1398 if (_active_notes[note->note()]) {
1399 CanvasNote* const old_rect = _active_notes[note->note()];
1400 boost::shared_ptr<NoteType> old_note = old_rect->note();
1401 old_rect->property_x2() = x;
1402 old_rect->property_outline_what() = (guint32) 0xF;
1404 _active_notes[note->note()] = ev;
1406 /* outline all but right edge */
1407 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1409 /* outline all edges */
1410 ev->property_outline_what() = (guint32) 0xF;
1415 MidiRegionView::update_hit (CanvasHit* ev)
1417 boost::shared_ptr<NoteType> note = ev->note();
1419 const framepos_t note_start_frames = beats_to_frames(note->time());
1420 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1421 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1422 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1427 /** Add a MIDI note to the view (with length).
1429 * If in sustained mode, notes with length 0 will be considered active
1430 * notes, and resolve_note should be called when the corresponding note off
1431 * event arrives, to properly display the note.
1434 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1436 CanvasNoteEvent* event = 0;
1438 assert(note->time() >= 0);
1439 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1441 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1443 if (midi_view()->note_mode() == Sustained) {
1445 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1447 update_note (ev_rect);
1451 MidiGhostRegion* gr;
1453 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1454 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1455 gr->add_note(ev_rect);
1459 } else if (midi_view()->note_mode() == Percussive) {
1461 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1463 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1465 update_hit (ev_diamond);
1474 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1475 note_selected(event, true);
1478 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1479 event->show_velocity();
1481 event->on_channel_selection_change(_last_channel_selection);
1482 _events.push_back(event);
1493 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1494 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1496 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1498 /* potentially extend region to hold new note */
1500 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1501 framepos_t region_end = _region->position() + _region->length() - 1;
1503 if (end_frame > region_end) {
1504 _region->set_length (end_frame - _region->position(), this);
1507 _marked_for_selection.clear ();
1510 start_diff_command (_("step add"));
1511 diff_add_note (new_note, true, false);
1514 // last_step_edit_note = new_note;
1518 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1520 change_note_lengths (false, false, beats, false, true);
1524 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1526 assert(program.time >= 0);
1528 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1529 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1531 double height = midi_stream_view()->contents_height();
1533 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1534 new CanvasProgramChange(*this, *group,
1539 _custom_device_mode,
1540 program.time, program.channel, program.value));
1542 // Show unless program change is beyond the region bounds
1543 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1549 _pgm_changes.push_back(pgm_change);
1553 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1555 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1556 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1558 if (msb_control != 0) {
1559 msb = int(msb_control->get_double(true, time));
1562 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1563 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1565 if (lsb_control != 0) {
1566 lsb = lsb_control->get_double(true, time);
1569 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1570 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1571 double program_number = -1.0;
1572 if (program_control != 0) {
1573 program_number = program_control->get_double(true, time);
1576 key.msb = (int) floor(msb + 0.5);
1577 key.lsb = (int) floor(lsb + 0.5);
1578 key.program_number = (int) floor(program_number + 0.5);
1579 assert(key.is_sane());
1584 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1586 // TODO: Get the real event here and alter them at the original times
1587 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1588 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1589 if (msb_control != 0) {
1590 msb_control->set_double(double(new_patch.msb), true, old_program.time);
1593 // TODO: Get the real event here and alter them at the original times
1594 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1595 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1596 if (lsb_control != 0) {
1597 lsb_control->set_double(double(new_patch.lsb), true, old_program.time);
1600 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1601 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1603 assert(program_control != 0);
1604 program_control->set_double(float(new_patch.program_number), true, old_program.time);
1606 _pgm_changes.clear ();
1607 display_program_changes (); // XXX would be nice to limit to just old_program.channel
1611 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1613 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1614 alter_program_change(program_change_event, new_patch);
1618 MidiRegionView::previous_program(CanvasProgramChange& program)
1620 if (program.program() < 127) {
1621 MIDI::Name::PatchPrimaryKey key;
1622 get_patch_key_at(program.event_time(), program.channel(), key);
1623 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1625 key.program_number++;
1626 alter_program_change(program_change_event, key);
1631 MidiRegionView::next_program(CanvasProgramChange& program)
1633 if (program.program() > 0) {
1634 MIDI::Name::PatchPrimaryKey key;
1635 get_patch_key_at(program.event_time(), program.channel(), key);
1636 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1638 key.program_number--;
1639 alter_program_change(program_change_event, key);
1644 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1646 if (_selection.empty()) {
1650 if (_selection.erase (cne) > 0) {
1651 cerr << "Erased a CNE from selection\n";
1656 MidiRegionView::delete_selection()
1658 if (_selection.empty()) {
1662 start_diff_command (_("delete selection"));
1664 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1665 if ((*i)->selected()) {
1666 _diff_command->remove((*i)->note());
1676 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1678 start_diff_command (_("delete note"));
1679 _diff_command->remove (n);
1682 trackview.editor().hide_verbose_canvas_cursor ();
1686 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1688 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1689 if ((*i)->selected() && (*i) != ev) {
1690 (*i)->set_selected(false);
1691 (*i)->hide_velocity();
1699 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1701 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1704 Selection::iterator tmp = i;
1707 (*i)->set_selected (false);
1708 _selection.erase (i);
1717 /* don't bother with removing this regionview from the editor selection,
1718 since we're about to add another note, and thus put/keep this
1719 regionview in the editor selection.
1722 if (!ev->selected()) {
1723 add_to_selection (ev);
1728 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1730 uint8_t low_note = 127;
1731 uint8_t high_note = 0;
1732 MidiModel::Notes& notes (_model->notes());
1733 _optimization_iterator = _events.begin();
1739 if (extend && _selection.empty()) {
1745 /* scan existing selection to get note range */
1747 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1748 if ((*i)->note()->note() < low_note) {
1749 low_note = (*i)->note()->note();
1751 if ((*i)->note()->note() > high_note) {
1752 high_note = (*i)->note()->note();
1756 low_note = min (low_note, notenum);
1757 high_note = max (high_note, notenum);
1760 no_sound_notes = true;
1762 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1764 boost::shared_ptr<NoteType> note (*n);
1765 CanvasNoteEvent* cne;
1766 bool select = false;
1768 if (((1 << note->channel()) & channel_mask) != 0) {
1770 if ((note->note() >= low_note && note->note() <= high_note)) {
1773 } else if (note->note() == notenum) {
1779 if ((cne = find_canvas_note (note)) != 0) {
1780 // extend is false because we've taken care of it,
1781 // since it extends by time range, not pitch.
1782 note_selected (cne, add, false);
1786 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1790 no_sound_notes = false;
1794 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1796 MidiModel::Notes& notes (_model->notes());
1797 _optimization_iterator = _events.begin();
1799 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1801 boost::shared_ptr<NoteType> note (*n);
1802 CanvasNoteEvent* cne;
1804 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1805 if ((cne = find_canvas_note (note)) != 0) {
1806 if (cne->selected()) {
1807 note_deselected (cne);
1809 note_selected (cne, true, false);
1817 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1820 clear_selection_except(ev);
1825 if (!ev->selected()) {
1826 add_to_selection (ev);
1830 /* find end of latest note selected, select all between that and the start of "ev" */
1832 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1833 Evoral::MusicalTime latest = 0;
1835 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1836 if ((*i)->note()->end_time() > latest) {
1837 latest = (*i)->note()->end_time();
1839 if ((*i)->note()->time() < earliest) {
1840 earliest = (*i)->note()->time();
1844 if (ev->note()->end_time() > latest) {
1845 latest = ev->note()->end_time();
1848 if (ev->note()->time() < earliest) {
1849 earliest = ev->note()->time();
1852 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1854 /* find notes entirely within OR spanning the earliest..latest range */
1856 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1857 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1858 add_to_selection (*i);
1862 /* if events were guaranteed to be time sorted, we could do this.
1863 but as of sept 10th 2009, they no longer are.
1866 if ((*i)->note()->time() > latest) {
1875 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1877 remove_from_selection (ev);
1881 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1891 // TODO: Make this faster by storing the last updated selection rect, and only
1892 // adjusting things that are in the area that appears/disappeared.
1893 // We probably need a tree to be able to find events in O(log(n)) time.
1895 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1897 /* check if any corner of the note is inside the rect
1900 1) this is computing "touched by", not "contained by" the rect.
1901 2) this does not require that events be sorted in time.
1904 const double ix1 = (*i)->x1();
1905 const double ix2 = (*i)->x2();
1906 const double iy1 = (*i)->y1();
1907 const double iy2 = (*i)->y2();
1909 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1910 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1911 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1912 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1915 if (!(*i)->selected()) {
1916 add_to_selection (*i);
1918 } else if ((*i)->selected()) {
1919 // Not inside rectangle
1920 remove_from_selection (*i);
1926 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1928 Selection::iterator i = _selection.find (ev);
1930 if (i != _selection.end()) {
1931 _selection.erase (i);
1934 ev->set_selected (false);
1935 ev->hide_velocity ();
1937 if (_selection.empty()) {
1938 PublicEditor& editor (trackview.editor());
1939 editor.get_selection().remove (this);
1944 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
1946 bool add_mrv_selection = false;
1948 if (_selection.empty()) {
1949 add_mrv_selection = true;
1952 if (_selection.insert (ev).second) {
1953 ev->set_selected (true);
1954 play_midi_note ((ev)->note());
1957 if (add_mrv_selection) {
1958 PublicEditor& editor (trackview.editor());
1959 editor.get_selection().add (this);
1964 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
1966 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
1967 PossibleChord to_play;
1968 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1970 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1971 if ((*i)->note()->time() < earliest) {
1972 earliest = (*i)->note()->time();
1976 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1977 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
1978 to_play.push_back ((*i)->note());
1980 (*i)->move_event(dx, dy);
1983 if (dy && !_selection.empty() && !no_sound_notes && trackview.editor().sound_notes()) {
1985 if (to_play.size() > 1) {
1987 PossibleChord shifted;
1989 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
1990 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
1991 moved_note->set_note (moved_note->note() + cumulative_dy);
1992 shifted.push_back (moved_note);
1995 play_midi_chord (shifted);
1997 } else if (!to_play.empty()) {
1999 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2000 moved_note->set_note (moved_note->note() + cumulative_dy);
2001 play_midi_note (moved_note);
2007 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2009 assert (!_selection.empty());
2011 uint8_t lowest_note_in_selection = 127;
2012 uint8_t highest_note_in_selection = 0;
2013 uint8_t highest_note_difference = 0;
2015 // find highest and lowest notes first
2017 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2018 uint8_t pitch = (*i)->note()->note();
2019 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2020 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2024 cerr << "dnote: " << (int) dnote << endl;
2025 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2026 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2027 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2028 << int(highest_note_in_selection) << endl;
2029 cerr << "selection size: " << _selection.size() << endl;
2030 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2033 // Make sure the note pitch does not exceed the MIDI standard range
2034 if (highest_note_in_selection + dnote > 127) {
2035 highest_note_difference = highest_note_in_selection - 127;
2038 start_diff_command(_("move notes"));
2040 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2042 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2048 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
2050 uint8_t original_pitch = (*i)->note()->note();
2051 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2053 // keep notes in standard midi range
2054 clamp_to_0_127(new_pitch);
2056 // keep original pitch if note is dragged outside valid midi range
2057 if ((original_pitch != 0 && new_pitch == 0)
2058 || (original_pitch != 127 && new_pitch == 127)) {
2059 new_pitch = original_pitch;
2062 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2063 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2065 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
2070 // care about notes being moved beyond the upper/lower bounds on the canvas
2071 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2072 highest_note_in_selection > midi_stream_view()->highest_note()) {
2073 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2078 MidiRegionView::snap_pixel_to_frame(double x)
2080 PublicEditor& editor = trackview.editor();
2081 // x is region relative, convert it to global absolute frames
2082 framepos_t frame = editor.pixel_to_frame(x) + _region->position();
2083 editor.snap_to(frame);
2084 return frame - _region->position(); // convert back to region relative
2088 MidiRegionView::snap_frame_to_frame(framepos_t x)
2090 PublicEditor& editor = trackview.editor();
2091 // x is region relative, convert it to global absolute frames
2092 framepos_t frame = x + _region->position();
2093 editor.snap_to(frame);
2094 return frame - _region->position(); // convert back to region relative
2098 MidiRegionView::snap_to_pixel(double x)
2100 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2104 MidiRegionView::get_position_pixels()
2106 framepos_t region_frame = get_position();
2107 return trackview.editor().frame_to_pixel(region_frame);
2111 MidiRegionView::get_end_position_pixels()
2113 framepos_t frame = get_position() + get_duration ();
2114 return trackview.editor().frame_to_pixel(frame);
2118 MidiRegionView::beats_to_frames(double beats) const
2120 return _time_converter.to(beats);
2124 MidiRegionView::frames_to_beats(framepos_t frames) const
2126 return _time_converter.from(frames);
2130 MidiRegionView::begin_resizing (bool /*at_front*/)
2132 _resize_data.clear();
2134 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2135 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2137 // only insert CanvasNotes into the map
2139 NoteResizeData *resize_data = new NoteResizeData();
2140 resize_data->canvas_note = note;
2142 // create a new SimpleRect from the note which will be the resize preview
2143 SimpleRect *resize_rect = new SimpleRect(
2144 *group, note->x1(), note->y1(), note->x2(), note->y2());
2146 // calculate the colors: get the color settings
2147 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2148 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2151 // make the resize preview notes more transparent and bright
2152 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2154 // calculate color based on note velocity
2155 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2156 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2160 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2161 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2163 resize_data->resize_rect = resize_rect;
2164 _resize_data.push_back(resize_data);
2169 /** Update resizing notes while user drags.
2170 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2171 * @param at_front which end of the note (true == note on, false == note off)
2172 * @param delta_x change in mouse position since the start of the drag
2173 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2174 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2175 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2176 * as the \a primary note.
2179 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2181 bool cursor_set = false;
2183 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2184 SimpleRect* resize_rect = (*i)->resize_rect;
2185 CanvasNote* canvas_note = (*i)->canvas_note;
2190 current_x = canvas_note->x1() + delta_x;
2192 current_x = primary->x1() + delta_x;
2196 current_x = canvas_note->x2() + delta_x;
2198 current_x = primary->x2() + delta_x;
2203 resize_rect->property_x1() = snap_to_pixel(current_x);
2204 resize_rect->property_x2() = canvas_note->x2();
2206 resize_rect->property_x2() = snap_to_pixel(current_x);
2207 resize_rect->property_x1() = canvas_note->x1();
2213 beats = snap_pixel_to_frame (current_x);
2214 beats = frames_to_beats (beats);
2219 if (beats < canvas_note->note()->end_time()) {
2220 len = canvas_note->note()->time() - beats;
2221 len += canvas_note->note()->length();
2226 if (beats >= canvas_note->note()->time()) {
2227 len = beats - canvas_note->note()->time();
2234 snprintf (buf, sizeof (buf), "%.3g beats", len);
2235 trackview.editor().show_verbose_canvas_cursor_with (buf);
2244 /** Finish resizing notes when the user releases the mouse button.
2245 * Parameters the same as for \a update_resizing().
2248 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2250 start_diff_command(_("resize notes"));
2252 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2253 CanvasNote* canvas_note = (*i)->canvas_note;
2254 SimpleRect* resize_rect = (*i)->resize_rect;
2259 current_x = canvas_note->x1() + delta_x;
2261 current_x = primary->x1() + delta_x;
2265 current_x = canvas_note->x2() + delta_x;
2267 current_x = primary->x2() + delta_x;
2271 current_x = snap_pixel_to_frame (current_x);
2272 current_x = frames_to_beats (current_x);
2274 if (at_front && current_x < canvas_note->note()->end_time()) {
2275 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2277 double len = canvas_note->note()->time() - current_x;
2278 len += canvas_note->note()->length();
2281 /* XXX convert to beats */
2282 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2287 double len = current_x - canvas_note->note()->time();
2290 /* XXX convert to beats */
2291 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2299 _resize_data.clear();
2304 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2306 diff_add_change (event, MidiModel::DiffCommand::Channel, (uint8_t) channel);
2310 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2312 uint8_t new_velocity;
2315 new_velocity = event->note()->velocity() + velocity;
2316 clamp_to_0_127(new_velocity);
2318 new_velocity = velocity;
2321 event->set_selected (event->selected()); // change color
2323 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2327 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2332 new_note = event->note()->note() + note;
2337 clamp_to_0_127 (new_note);
2338 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2342 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2344 bool change_start = false;
2345 bool change_length = false;
2346 Evoral::MusicalTime new_start = 0;
2347 Evoral::MusicalTime new_length = 0;
2349 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2351 front_delta: if positive - move the start of the note later in time (shortening it)
2352 if negative - move the start of the note earlier in time (lengthening it)
2354 end_delta: if positive - move the end of the note later in time (lengthening it)
2355 if negative - move the end of the note earlier in time (shortening it)
2359 if (front_delta < 0) {
2361 if (event->note()->time() < -front_delta) {
2364 new_start = event->note()->time() + front_delta; // moves earlier
2367 /* start moved toward zero, so move the end point out to where it used to be.
2368 Note that front_delta is negative, so this increases the length.
2371 new_length = event->note()->length() - front_delta;
2372 change_start = true;
2373 change_length = true;
2377 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2379 if (new_pos < event->note()->end_time()) {
2380 new_start = event->note()->time() + front_delta;
2381 /* start moved toward the end, so move the end point back to where it used to be */
2382 new_length = event->note()->length() - front_delta;
2383 change_start = true;
2384 change_length = true;
2391 bool can_change = true;
2392 if (end_delta < 0) {
2393 if (event->note()->length() < -end_delta) {
2399 new_length = event->note()->length() + end_delta;
2400 change_length = true;
2405 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2408 if (change_length) {
2409 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2414 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2416 Evoral::MusicalTime new_time;
2420 if (event->note()->time() < -delta) {
2423 new_time = event->note()->time() + delta;
2426 new_time = event->note()->time() + delta;
2432 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2436 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2438 diff_add_change (event, MidiModel::DiffCommand::Length, t);
2442 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2446 if (_selection.empty()) {
2461 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2462 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2468 start_diff_command(_("change velocities"));
2470 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2471 Selection::iterator next = i;
2473 change_note_velocity (*i, delta, true);
2479 if (!_selection.empty()) {
2481 snprintf (buf, sizeof (buf), "Vel %d",
2482 (int) (*_selection.begin())->note()->velocity());
2483 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 10);
2489 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2491 if (_selection.empty()) {
2508 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2510 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2514 if ((int8_t) (*i)->note()->note() + delta > 127) {
2521 start_diff_command (_("transpose"));
2523 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2524 Selection::iterator next = i;
2526 change_note_note (*i, delta, true);
2534 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2540 /* grab the current grid distance */
2542 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2544 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2545 cerr << "Grid type not available as beats - TO BE FIXED\n";
2555 start_diff_command (_("change note lengths"));
2557 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2558 Selection::iterator next = i;
2561 /* note the negation of the delta for start */
2563 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2572 MidiRegionView::nudge_notes (bool forward)
2574 if (_selection.empty()) {
2578 /* pick a note as the point along the timeline to get the nudge distance.
2579 its not necessarily the earliest note, so we may want to pull the notes out
2580 into a vector and sort before using the first one.
2583 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2585 framepos_t distance;
2587 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2589 /* grid is off - use nudge distance */
2591 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2597 framepos_t next_pos = ref_point;
2600 if (max_framepos - 1 < next_pos) {
2604 if (next_pos == 0) {
2610 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2611 distance = ref_point - next_pos;
2614 if (distance == 0) {
2618 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2624 start_diff_command (_("nudge"));
2626 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2627 Selection::iterator next = i;
2629 change_note_time (*i, delta, true);
2637 MidiRegionView::change_channel(uint8_t channel)
2639 start_diff_command(_("change channel"));
2640 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2641 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2649 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2651 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2653 pre_enter_cursor = editor->get_canvas_cursor ();
2655 if (_mouse_state == SelectTouchDragging) {
2656 note_selected (ev, true);
2659 show_verbose_canvas_cursor (ev->note ());
2663 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent* note)
2665 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2667 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2668 (*i)->hide_velocity ();
2671 editor->hide_verbose_canvas_cursor ();
2673 if (pre_enter_cursor) {
2674 editor->set_canvas_cursor (pre_enter_cursor);
2675 pre_enter_cursor = 0;
2680 MidiRegionView::note_mouse_position (float x_fraction, float y_fraction, bool can_set_cursor)
2682 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2684 if (x_fraction > 0.0 && x_fraction < 0.25) {
2685 editor->set_canvas_cursor (editor->left_side_trim_cursor);
2686 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
2687 editor->set_canvas_cursor (editor->right_side_trim_cursor);
2689 if (pre_enter_cursor && can_set_cursor) {
2690 editor->set_canvas_cursor (pre_enter_cursor);
2696 MidiRegionView::set_frame_color()
2699 if (_selected && should_show_selection) {
2700 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2702 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2708 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2712 case FilterChannels:
2713 _force_channel = -1;
2716 _force_channel = mask;
2717 mask = 0xFFFF; // Show all notes as active (below)
2720 // Update notes for selection
2721 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2722 (*i)->on_channel_selection_change(mask);
2725 _last_channel_selection = mask;
2729 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2731 _model_name = model;
2732 _custom_device_mode = custom_device_mode;
2737 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2739 if (_selection.empty()) {
2743 PublicEditor& editor (trackview.editor());
2748 editor.get_cut_buffer().add (selection_as_cut_buffer());
2756 start_diff_command();
2758 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2764 diff_remove_note (*i);
2774 MidiRegionView::selection_as_cut_buffer () const
2778 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2779 NoteType* n = (*i)->note().get();
2780 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2783 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2790 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
2796 start_diff_command (_("paste"));
2798 Evoral::MusicalTime beat_delta;
2799 Evoral::MusicalTime paste_pos_beats;
2800 Evoral::MusicalTime duration;
2801 Evoral::MusicalTime end_point = 0;
2803 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2804 paste_pos_beats = frames_to_beats (pos - _region->position());
2805 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2806 paste_pos_beats = 0;
2810 for (int n = 0; n < (int) times; ++n) {
2812 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2814 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2815 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2817 /* make all newly added notes selected */
2819 diff_add_note (copied_note, true);
2820 end_point = copied_note->end_time();
2823 paste_pos_beats += duration;
2826 /* if we pasted past the current end of the region, extend the region */
2828 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
2829 framepos_t region_end = _region->position() + _region->length() - 1;
2831 if (end_frame > region_end) {
2833 trackview.session()->begin_reversible_command (_("paste"));
2835 _region->clear_changes ();
2836 _region->set_length (end_frame, this);
2837 trackview.session()->add_command (new StatefulDiffCommand (_region));
2843 struct EventNoteTimeEarlyFirstComparator {
2844 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2845 return a->note()->time() < b->note()->time();
2850 MidiRegionView::time_sort_events ()
2852 if (!_sort_needed) {
2856 EventNoteTimeEarlyFirstComparator cmp;
2859 _sort_needed = false;
2863 MidiRegionView::goto_next_note ()
2865 // framepos_t pos = -1;
2866 bool use_next = false;
2868 if (_events.back()->selected()) {
2872 time_sort_events ();
2874 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2875 if ((*i)->selected()) {
2878 } else if (use_next) {
2880 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2885 /* use the first one */
2887 unique_select (_events.front());
2892 MidiRegionView::goto_previous_note ()
2894 // framepos_t pos = -1;
2895 bool use_next = false;
2897 if (_events.front()->selected()) {
2901 time_sort_events ();
2903 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2904 if ((*i)->selected()) {
2907 } else if (use_next) {
2909 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2914 /* use the last one */
2916 unique_select (*(_events.rbegin()));
2920 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2922 bool had_selected = false;
2924 time_sort_events ();
2926 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2927 if ((*i)->selected()) {
2928 selected.insert ((*i)->note());
2929 had_selected = true;
2933 if (allow_all_if_none_selected && !had_selected) {
2934 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2935 selected.insert ((*i)->note());
2941 MidiRegionView::update_ghost_note (double x, double y)
2947 framepos_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
2948 trackview.editor().snap_to (f);
2949 f -= _region->position ();
2952 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
2957 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
2959 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
2960 _ghost_note->note()->set_length (length);
2961 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
2963 update_note (_ghost_note);
2965 show_verbose_canvas_cursor (_ghost_note->note ());
2969 MidiRegionView::create_ghost_note (double x, double y)
2974 boost::shared_ptr<NoteType> g (new NoteType);
2975 _ghost_note = new NoEventCanvasNote (*this, *group, g);
2976 update_ghost_note (x, y);
2977 _ghost_note->show ();
2982 show_verbose_canvas_cursor (_ghost_note->note ());
2986 MidiRegionView::snap_changed ()
2992 create_ghost_note (_last_ghost_x, _last_ghost_y);
2996 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
2999 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
3000 Evoral::midi_note_name (n->note()).c_str(),
3002 (int) n->velocity());
3003 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 20);
3007 MidiRegionView::drop_down_keys ()
3009 _mouse_state = None;
3013 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double x, double y)
3015 double note = midi_stream_view()->y_to_note(y);
3017 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3019 cerr << "Selecting by position\n";
3021 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3023 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3024 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3025 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3026 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3031 bool add_mrv_selection = false;
3033 if (_selection.empty()) {
3034 add_mrv_selection = true;
3037 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3038 if (_selection.insert (*i).second) {
3039 (*i)->set_selected (true);
3043 if (add_mrv_selection) {
3044 PublicEditor& editor (trackview.editor());
3045 editor.get_selection().add (this);
3050 MidiRegionView::color_handler ()
3052 RegionView::color_handler ();
3054 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3055 (*i)->set_selected ((*i)->selected()); // will change color
3058 /* XXX probably more to do here */
3062 MidiRegionView::enable_display (bool yn)
3064 RegionView::enable_display (yn);
3071 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3073 if (_step_edit_cursor == 0) {
3074 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3076 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3077 _step_edit_cursor->property_y1() = 0;
3078 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3079 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3080 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3083 move_step_edit_cursor (pos);
3084 _step_edit_cursor->show ();
3088 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3090 _step_edit_cursor_position = pos;
3092 if (_step_edit_cursor) {
3093 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3094 _step_edit_cursor->property_x1() = pixel;
3095 set_step_edit_cursor_width (_step_edit_cursor_width);
3100 MidiRegionView::hide_step_edit_cursor ()
3102 if (_step_edit_cursor) {
3103 _step_edit_cursor->hide ();
3108 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3110 _step_edit_cursor_width = beats;
3112 if (_step_edit_cursor) {
3113 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));