2 Copyright (C) 2001-2007 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <gtkmm2ext/gtk_ui.h>
29 #include <sigc++/signal.h>
31 #include "pbd/memento_command.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "ardour/playlist.h"
35 #include "ardour/tempo.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_model.h"
39 #include "ardour/midi_patch_manager.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIParameters.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "automation_region_view.h"
48 #include "automation_time_axis.h"
49 #include "canvas-hit.h"
50 #include "canvas-note.h"
51 #include "canvas-program-change.h"
53 #include "ghostregion.h"
54 #include "gui_thread.h"
56 #include "midi_cut_buffer.h"
57 #include "midi_list_editor.h"
58 #include "midi_region_view.h"
59 #include "midi_streamview.h"
60 #include "midi_time_axis.h"
61 #include "midi_time_axis.h"
62 #include "midi_util.h"
63 #include "note_player.h"
64 #include "public_editor.h"
65 #include "rgb_macros.h"
66 #include "selection.h"
67 #include "simpleline.h"
68 #include "streamview.h"
70 #include "mouse_cursors.h"
74 using namespace ARDOUR;
76 using namespace Editing;
77 using namespace ArdourCanvas;
78 using Gtkmm2ext::Keyboard;
80 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
81 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
82 : RegionView (parent, tv, r, spu, basic_color)
84 , _last_channel_selection(0xFFFF)
85 , _current_range_min(0)
86 , _current_range_max(0)
87 , _model_name(string())
88 , _custom_device_mode(string())
90 , _note_group(new ArdourCanvas::Group(*parent))
94 , _step_edit_cursor (0)
95 , _step_edit_cursor_width (1.0)
96 , _step_edit_cursor_position (0.0)
100 , _optimization_iterator (_events.end())
102 , no_sound_notes (false)
105 , pre_enter_cursor (0)
107 _note_group->raise_to_top();
108 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
110 connect_to_diskstream ();
113 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
114 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
115 TimeAxisViewItem::Visibility visibility)
116 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
118 , _last_channel_selection(0xFFFF)
119 , _model_name(string())
120 , _custom_device_mode(string())
122 , _note_group(new ArdourCanvas::Group(*parent))
126 , _step_edit_cursor (0)
127 , _step_edit_cursor_width (1.0)
128 , _step_edit_cursor_position (0.0)
131 , _sort_needed (true)
132 , _optimization_iterator (_events.end())
134 , no_sound_notes (false)
138 _note_group->raise_to_top();
139 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
141 connect_to_diskstream ();
144 MidiRegionView::MidiRegionView (const MidiRegionView& other)
145 : sigc::trackable(other)
148 , _last_channel_selection(0xFFFF)
149 , _model_name(string())
150 , _custom_device_mode(string())
152 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
156 , _step_edit_cursor (0)
157 , _step_edit_cursor_width (1.0)
158 , _step_edit_cursor_position (0.0)
161 , _sort_needed (true)
162 , _optimization_iterator (_events.end())
164 , no_sound_notes (false)
171 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
172 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
177 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
178 : RegionView (other, boost::shared_ptr<Region> (region))
180 , _last_channel_selection(0xFFFF)
181 , _model_name(string())
182 , _custom_device_mode(string())
184 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
188 , _step_edit_cursor (0)
189 , _step_edit_cursor_width (1.0)
190 , _step_edit_cursor_position (0.0)
193 , _sort_needed (true)
194 , _optimization_iterator (_events.end())
196 , no_sound_notes (false)
203 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
204 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
210 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
212 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
214 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
215 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
219 midi_region()->midi_source(0)->load_model();
222 _model = midi_region()->midi_source(0)->model();
223 _enable_display = false;
225 RegionView::init (basic_color, false);
227 compute_colors (basic_color);
229 set_height (trackview.current_height());
232 region_sync_changed ();
233 region_resized (ARDOUR::bounds_change);
236 reset_width_dependent_items (_pixel_width);
240 _enable_display = true;
243 display_model (_model);
247 group->raise_to_top();
248 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
250 midi_view()->signal_channel_mode_changed().connect(
251 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
253 midi_view()->signal_midi_patch_settings_changed().connect(
254 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
256 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
258 connect_to_diskstream ();
262 MidiRegionView::connect_to_diskstream ()
264 midi_view()->midi_track()->DataRecorded.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::data_recorded, this, _1, _2), gui_context ());
268 MidiRegionView::canvas_event(GdkEvent* ev)
271 case GDK_ENTER_NOTIFY:
272 case GDK_LEAVE_NOTIFY:
273 _last_event_x = ev->crossing.x;
274 _last_event_y = ev->crossing.y;
276 case GDK_MOTION_NOTIFY:
277 _last_event_x = ev->motion.x;
278 _last_event_y = ev->motion.y;
282 if (!trackview.editor().internal_editing()) {
286 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
287 to its items, which means that ev->type == GDK_SCROLL will never be seen
292 return scroll (&ev->scroll);
295 return key_press (&ev->key);
297 case GDK_KEY_RELEASE:
298 return key_release (&ev->key);
300 case GDK_BUTTON_PRESS:
301 return button_press (&ev->button);
303 case GDK_2BUTTON_PRESS:
306 case GDK_BUTTON_RELEASE:
307 return button_release (&ev->button);
309 case GDK_ENTER_NOTIFY:
310 return enter_notify (&ev->crossing);
312 case GDK_LEAVE_NOTIFY:
313 return leave_notify (&ev->crossing);
315 case GDK_MOTION_NOTIFY:
316 return motion (&ev->motion);
326 MidiRegionView::remove_ghost_note ()
333 MidiRegionView::enter_notify (GdkEventCrossing* ev)
335 trackview.editor().MouseModeChanged.connect (
336 _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
339 Keyboard::magic_widget_grab_focus();
342 if (trackview.editor().current_mouse_mode() == MouseRange) {
343 create_ghost_note (ev->x, ev->y);
350 MidiRegionView::leave_notify (GdkEventCrossing*)
352 _mouse_mode_connection.disconnect ();
354 trackview.editor().hide_verbose_canvas_cursor ();
355 remove_ghost_note ();
360 MidiRegionView::mouse_mode_changed ()
362 if (trackview.editor().current_mouse_mode() == MouseRange && trackview.editor().internal_editing()) {
363 create_ghost_note (_last_event_x, _last_event_y);
365 remove_ghost_note ();
366 trackview.editor().hide_verbose_canvas_cursor ();
371 MidiRegionView::button_press (GdkEventButton* ev)
375 group->w2i (_last_x, _last_y);
377 if (_mouse_state != SelectTouchDragging && ev->button == 1) {
378 _pressed_button = ev->button;
379 _mouse_state = Pressed;
383 _pressed_button = ev->button;
389 MidiRegionView::button_release (GdkEventButton* ev)
391 double event_x, event_y;
392 framepos_t event_frame = 0;
396 group->w2i(event_x, event_y);
397 group->ungrab(ev->time);
398 event_frame = trackview.editor().pixel_to_frame(event_x);
400 if (ev->button == 3) {
402 } else if (_pressed_button != 1) {
406 switch (_mouse_state) {
407 case Pressed: // Clicked
408 switch (trackview.editor().current_mouse_mode()) {
412 maybe_select_by_position (ev, event_x, event_y);
418 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
422 create_note_at (event_x, event_y, beats, true);
430 case SelectRectDragging: // Select drag done
436 case AddDragging: // Add drag done
438 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
439 const double x = _drag_rect->property_x1();
440 const double length = trackview.editor().pixel_to_frame
441 (_drag_rect->property_x2() - _drag_rect->property_x1());
443 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), false);
449 create_ghost_note (ev->x, ev->y);
459 MidiRegionView::motion (GdkEventMotion* ev)
461 double event_x, event_y;
462 framepos_t event_frame = 0;
466 group->w2i(event_x, event_y);
468 // convert event_x to global frame
469 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
470 trackview.editor().snap_to(event_frame);
471 // convert event_frame back to local coordinates relative to position
472 event_frame -= _region->position();
475 update_ghost_note (ev->x, ev->y);
478 /* any motion immediately hides velocity text that may have been visible */
480 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
481 (*i)->hide_velocity ();
484 switch (_mouse_state) {
485 case Pressed: // Maybe start a drag, if we've moved a bit
487 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
488 /* no appreciable movement since the button was pressed */
493 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
494 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
495 Gdk::Cursor(Gdk::FLEUR), ev->time);
498 _drag_start_x = event_x;
499 _drag_start_y = event_y;
501 _drag_rect = new ArdourCanvas::SimpleRect(*group);
502 _drag_rect->property_x1() = event_x;
503 _drag_rect->property_y1() = event_y;
504 _drag_rect->property_x2() = event_x;
505 _drag_rect->property_y2() = event_y;
506 _drag_rect->property_outline_what() = 0xFF;
507 _drag_rect->property_outline_color_rgba()
508 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
509 _drag_rect->property_fill_color_rgba()
510 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
512 _mouse_state = SelectRectDragging;
515 // Add note drag start
516 } else if (trackview.editor().internal_editing()) {
521 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
522 Gdk::Cursor(Gdk::FLEUR), ev->time);
525 _drag_start_x = event_x;
526 _drag_start_y = event_y;
528 _drag_rect = new ArdourCanvas::SimpleRect(*group);
529 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
531 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
532 midi_stream_view()->y_to_note(event_y));
533 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
534 _drag_rect->property_y2() = _drag_rect->property_y1()
535 + floor(midi_stream_view()->note_height());
536 _drag_rect->property_outline_what() = 0xFF;
537 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
538 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
540 _mouse_state = AddDragging;
546 case SelectRectDragging: // Select drag motion
547 case AddDragging: // Add note drag motion
551 GdkModifierType state;
552 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
557 if (_mouse_state == AddDragging)
558 event_x = trackview.editor().frame_to_pixel(event_frame);
561 if (event_x > _drag_start_x)
562 _drag_rect->property_x2() = event_x;
564 _drag_rect->property_x1() = event_x;
567 if (_drag_rect && _mouse_state == SelectRectDragging) {
568 if (event_y > _drag_start_y)
569 _drag_rect->property_y2() = event_y;
571 _drag_rect->property_y1() = event_y;
573 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
579 case SelectTouchDragging:
591 MidiRegionView::scroll (GdkEventScroll* ev)
593 if (_selection.empty()) {
597 trackview.editor().hide_verbose_canvas_cursor ();
599 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
601 if (ev->direction == GDK_SCROLL_UP) {
602 change_velocities (true, fine, false);
603 } else if (ev->direction == GDK_SCROLL_DOWN) {
604 change_velocities (false, fine, false);
610 MidiRegionView::key_press (GdkEventKey* ev)
612 /* since GTK bindings are generally activated on press, and since
613 detectable auto-repeat is the name of the game and only sends
614 repeated presses, carry out key actions at key press, not release.
617 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
618 _mouse_state = SelectTouchDragging;
621 } else if (ev->keyval == GDK_Escape) {
625 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
627 bool start = (ev->keyval == GDK_comma);
628 bool end = (ev->keyval == GDK_period);
629 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
630 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
632 change_note_lengths (fine, shorter, 0.0, start, end);
636 } else if (ev->keyval == GDK_Delete) {
641 } else if (ev->keyval == GDK_Tab) {
643 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
644 goto_previous_note ();
650 } else if (ev->keyval == GDK_Up) {
652 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
653 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
655 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
656 change_velocities (true, fine, allow_smush);
658 transpose (true, fine, allow_smush);
662 } else if (ev->keyval == GDK_Down) {
664 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
665 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
667 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
668 change_velocities (false, fine, allow_smush);
670 transpose (false, fine, allow_smush);
674 } else if (ev->keyval == GDK_Left) {
679 } else if (ev->keyval == GDK_Right) {
684 } else if (ev->keyval == GDK_Control_L) {
693 MidiRegionView::key_release (GdkEventKey* ev)
695 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
703 MidiRegionView::show_list_editor ()
706 _list_editor = new MidiListEditor (trackview.session(), midi_region());
708 _list_editor->present ();
711 /** Add a note to the model, and the view, at a canvas (click) coordinate.
712 * \param x horizontal position in pixels
713 * \param y vertical position in pixels
714 * \param length duration of the note in beats, which will be snapped to the grid
715 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
718 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
720 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
721 MidiStreamView* const view = mtv->midi_view();
723 double note = midi_stream_view()->y_to_note(y);
726 assert(note <= 127.0);
728 // Start of note in frames relative to region start
729 framepos_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
730 assert(start_frames >= 0);
733 length = frames_to_beats(
734 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
736 assert (length != 0);
739 length = frames_to_beats (beats_to_frames (length) - 1);
742 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
746 /* pick the highest selected channel, unless all channels are selected,
747 which is interpreted to mean channel 1 (zero)
750 for (uint16_t i = 0; i < 16; ++i) {
751 if (chn_mask & (1<<i)) {
761 const boost::shared_ptr<NoteType> new_note (new NoteType (channel,
762 frames_to_beats(start_frames + _region->start()), length,
763 (uint8_t)note, 0x40));
765 if (_model->contains (new_note)) {
769 view->update_note_range(new_note->note());
771 MidiModel::DiffCommand* cmd = _model->new_diff_command("add note");
773 _model->apply_command(*trackview.session(), cmd);
775 play_midi_note (new_note);
779 MidiRegionView::clear_events()
784 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
785 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
790 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
795 _pgm_changes.clear();
797 _optimization_iterator = _events.end();
801 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
805 content_connection.disconnect ();
806 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
810 if (_enable_display) {
816 MidiRegionView::start_diff_command(string name)
818 if (!_diff_command) {
819 _diff_command = _model->new_diff_command(name);
824 MidiRegionView::diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
827 _diff_command->add(note);
830 _marked_for_selection.insert(note);
833 _marked_for_velocity.insert(note);
838 MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
840 if (_diff_command && ev->note()) {
841 _diff_command->remove(ev->note());
846 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
847 MidiModel::DiffCommand::Property property,
851 _diff_command->change (ev->note(), property, val);
856 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
857 MidiModel::DiffCommand::Property property,
858 Evoral::MusicalTime val)
861 _diff_command->change (ev->note(), property, val);
866 MidiRegionView::apply_diff ()
870 if (!_diff_command) {
874 if ((add_or_remove = _diff_command->adds_or_removes())) {
875 // Mark all selected notes for selection when model reloads
876 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
877 _marked_for_selection.insert((*i)->note());
881 _model->apply_command(*trackview.session(), _diff_command);
883 midi_view()->midi_track()->playlist_modified();
886 _marked_for_selection.clear();
889 _marked_for_velocity.clear();
893 MidiRegionView::apply_diff_as_subcommand()
897 if (!_diff_command) {
901 if ((add_or_remove = _diff_command->adds_or_removes())) {
902 // Mark all selected notes for selection when model reloads
903 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
904 _marked_for_selection.insert((*i)->note());
908 _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
910 midi_view()->midi_track()->playlist_modified();
913 _marked_for_selection.clear();
915 _marked_for_velocity.clear();
920 MidiRegionView::abort_command()
922 delete _diff_command;
928 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
930 if (_optimization_iterator != _events.end()) {
931 ++_optimization_iterator;
934 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
935 return *_optimization_iterator;
938 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
939 if ((*_optimization_iterator)->note() == note) {
940 return *_optimization_iterator;
948 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
950 MidiModel::Notes notes;
951 _model->get_notes (notes, op, val, chan_mask);
953 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
954 CanvasNoteEvent* cne = find_canvas_note (*n);
962 MidiRegionView::redisplay_model()
964 // Don't redisplay the model if we're currently recording and displaying that
970 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
974 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
978 MidiModel::ReadLock lock(_model->read_lock());
980 MidiModel::Notes& notes (_model->notes());
981 _optimization_iterator = _events.begin();
983 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
985 boost::shared_ptr<NoteType> note (*n);
986 CanvasNoteEvent* cne;
989 if (note_in_region_range (note, visible)) {
991 if ((cne = find_canvas_note (note)) != 0) {
998 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1000 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1012 add_note (note, visible);
1017 if ((cne = find_canvas_note (note)) != 0) {
1025 /* remove note items that are no longer valid */
1027 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1028 if (!(*i)->valid ()) {
1030 i = _events.erase (i);
1036 _pgm_changes.clear();
1040 display_program_changes();
1042 _marked_for_selection.clear ();
1043 _marked_for_velocity.clear ();
1045 /* we may have caused _events to contain things out of order (e.g. if a note
1046 moved earlier or later). we don't generally need them in time order, but
1047 make a note that a sort is required for those cases that require it.
1050 _sort_needed = true;
1054 MidiRegionView::display_program_changes()
1056 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1057 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1059 for (uint8_t i = 0; i < 16; ++i) {
1060 if (chn_mask & (1<<i)) {
1061 display_program_changes_on_channel (i);
1067 MidiRegionView::display_program_changes_on_channel(uint8_t channel)
1069 boost::shared_ptr<Evoral::Control> control =
1070 _model->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation, channel));
1076 Glib::Mutex::Lock lock (control->list()->lock());
1078 for (AutomationList::const_iterator event = control->list()->begin();
1079 event != control->list()->end(); ++event) {
1080 double event_time = (*event)->when;
1081 double program_number = floor((*event)->value + 0.5);
1083 // Get current value of bank select MSB at time of the program change
1084 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1085 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1087 if (msb_control != 0) {
1088 msb = uint8_t(floor(msb_control->get_double(true, event_time) + 0.5));
1091 // Get current value of bank select LSB at time of the program change
1092 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1093 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1095 if (lsb_control != 0) {
1096 lsb = uint8_t(floor(lsb_control->get_double(true, event_time) + 0.5));
1099 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
1101 boost::shared_ptr<MIDI::Name::Patch> patch =
1102 MIDI::Name::MidiPatchManager::instance().find_patch(
1103 _model_name, _custom_device_mode, channel, patch_key);
1105 PCEvent program_change(event_time, uint8_t(program_number), channel);
1108 add_pgm_change(program_change, patch->name());
1111 // program_number is zero-based: convert to one-based
1112 snprintf(buf, 4, "%d", int(program_number+1));
1113 add_pgm_change(program_change, buf);
1119 MidiRegionView::display_sysexes()
1121 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1122 Evoral::MusicalTime time = (*i)->time();
1127 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1128 str << int((*i)->buffer()[b]);
1129 if (b != (*i)->size() -1) {
1133 string text = str.str();
1135 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1137 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1139 double height = midi_stream_view()->contents_height();
1141 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1142 new CanvasSysEx(*this, *group, text, height, x, 1.0));
1144 // Show unless program change is beyond the region bounds
1145 if (time - _region->start() >= _region->length() || time < _region->start()) {
1151 _sys_exes.push_back(sysex);
1156 MidiRegionView::~MidiRegionView ()
1158 in_destructor = true;
1160 trackview.editor().hide_verbose_canvas_cursor ();
1162 note_delete_connection.disconnect ();
1164 delete _list_editor;
1166 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1168 if (_active_notes) {
1176 delete _diff_command;
1177 delete _step_edit_cursor;
1181 MidiRegionView::region_resized (const PropertyChange& what_changed)
1183 RegionView::region_resized(what_changed);
1185 if (what_changed.contains (ARDOUR::Properties::position)) {
1186 set_duration(_region->length(), 0);
1187 if (_enable_display) {
1194 MidiRegionView::reset_width_dependent_items (double pixel_width)
1196 RegionView::reset_width_dependent_items(pixel_width);
1197 assert(_pixel_width == pixel_width);
1199 if (_enable_display) {
1203 move_step_edit_cursor (_step_edit_cursor_position);
1204 set_step_edit_cursor_width (_step_edit_cursor_width);
1208 MidiRegionView::set_height (double height)
1210 static const double FUDGE = 2.0;
1211 const double old_height = _height;
1212 RegionView::set_height(height);
1213 _height = height - FUDGE;
1215 apply_note_range(midi_stream_view()->lowest_note(),
1216 midi_stream_view()->highest_note(),
1217 height != old_height + FUDGE);
1220 name_pixbuf->raise_to_top();
1223 for (PgmChanges::iterator x = _pgm_changes.begin(); x != _pgm_changes.end(); ++x) {
1224 (*x)->set_height (midi_stream_view()->contents_height());
1227 if (_step_edit_cursor) {
1228 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1233 /** Apply the current note range from the stream view
1234 * by repositioning/hiding notes as necessary
1237 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1239 if (!_enable_display) {
1243 if (!force && _current_range_min == min && _current_range_max == max) {
1247 _current_range_min = min;
1248 _current_range_max = max;
1250 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1251 CanvasNoteEvent* event = *i;
1252 boost::shared_ptr<NoteType> note (event->note());
1254 if (note->note() < _current_range_min ||
1255 note->note() > _current_range_max) {
1261 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1263 const double y1 = midi_stream_view()->note_to_y(note->note());
1264 const double y2 = y1 + floor(midi_stream_view()->note_height());
1266 cnote->property_y1() = y1;
1267 cnote->property_y2() = y2;
1269 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1271 const double diamond_size = update_hit (chit);
1273 chit->set_height (diamond_size);
1279 MidiRegionView::add_ghost (TimeAxisView& tv)
1283 double unit_position = _region->position () / samples_per_unit;
1284 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1285 MidiGhostRegion* ghost;
1287 if (mtv && mtv->midi_view()) {
1288 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1289 to allow having midi notes on top of note lines and waveforms.
1291 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1293 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1296 ghost->set_height ();
1297 ghost->set_duration (_region->length() / samples_per_unit);
1298 ghosts.push_back (ghost);
1300 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1301 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1302 ghost->add_note(note);
1306 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1312 /** Begin tracking note state for successive calls to add_event
1315 MidiRegionView::begin_write()
1317 assert(!_active_notes);
1318 _active_notes = new CanvasNote*[128];
1319 for (unsigned i=0; i < 128; ++i) {
1320 _active_notes[i] = 0;
1325 /** Destroy note state for add_event
1328 MidiRegionView::end_write()
1330 delete[] _active_notes;
1332 _marked_for_selection.clear();
1333 _marked_for_velocity.clear();
1337 /** Resolve an active MIDI note (while recording).
1340 MidiRegionView::resolve_note(uint8_t note, double end_time)
1342 if (midi_view()->note_mode() != Sustained) {
1346 if (_active_notes && _active_notes[note]) {
1347 const framepos_t end_time_frames = beats_to_frames(end_time) - _region->start();
1348 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1349 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1350 _active_notes[note] = 0;
1355 /** Extend active notes to rightmost edge of region (if length is changed)
1358 MidiRegionView::extend_active_notes()
1360 if (!_active_notes) {
1364 for (unsigned i=0; i < 128; ++i) {
1365 if (_active_notes[i]) {
1366 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1373 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1375 if (no_sound_notes || !trackview.editor().sound_notes()) {
1379 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1381 if (!route_ui || !route_ui->midi_track()) {
1385 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1391 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1393 if (no_sound_notes || !trackview.editor().sound_notes()) {
1397 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1399 if (!route_ui || !route_ui->midi_track()) {
1403 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1405 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1414 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1416 const framepos_t note_start_frames = beats_to_frames(note->time());
1418 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1419 (note_start_frames < _region->start());
1421 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1422 (note->note() <= midi_stream_view()->highest_note());
1428 MidiRegionView::update_note (CanvasNote* ev)
1430 boost::shared_ptr<NoteType> note = ev->note();
1432 const framepos_t note_start_frames = beats_to_frames(note->time());
1434 /* trim note display to not overlap the end of its region */
1435 const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1437 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1438 const double y1 = midi_stream_view()->note_to_y(note->note());
1439 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1441 ev->property_x1() = x;
1442 ev->property_y1() = y1;
1443 if (note->length() > 0) {
1444 ev->property_x2() = note_endpixel;
1446 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1448 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1450 if (note->length() == 0) {
1451 if (_active_notes) {
1452 assert(note->note() < 128);
1453 // If this note is already active there's a stuck note,
1454 // finish the old note rectangle
1455 if (_active_notes[note->note()]) {
1456 CanvasNote* const old_rect = _active_notes[note->note()];
1457 boost::shared_ptr<NoteType> old_note = old_rect->note();
1458 old_rect->property_x2() = x;
1459 old_rect->property_outline_what() = (guint32) 0xF;
1461 _active_notes[note->note()] = ev;
1463 /* outline all but right edge */
1464 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1466 /* outline all edges */
1467 ev->property_outline_what() = (guint32) 0xF;
1472 MidiRegionView::update_hit (CanvasHit* ev)
1474 boost::shared_ptr<NoteType> note = ev->note();
1476 const framepos_t note_start_frames = beats_to_frames(note->time());
1477 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1478 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1479 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1483 return diamond_size;
1486 /** Add a MIDI note to the view (with length).
1488 * If in sustained mode, notes with length 0 will be considered active
1489 * notes, and resolve_note should be called when the corresponding note off
1490 * event arrives, to properly display the note.
1493 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1495 CanvasNoteEvent* event = 0;
1497 assert(note->time() >= 0);
1498 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1500 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1502 if (midi_view()->note_mode() == Sustained) {
1504 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1506 update_note (ev_rect);
1510 MidiGhostRegion* gr;
1512 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1513 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1514 gr->add_note(ev_rect);
1518 } else if (midi_view()->note_mode() == Percussive) {
1520 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1522 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1524 update_hit (ev_diamond);
1533 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1534 note_selected(event, true);
1537 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1538 event->show_velocity();
1540 event->on_channel_selection_change(_last_channel_selection);
1541 _events.push_back(event);
1552 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1553 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1555 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1557 /* potentially extend region to hold new note */
1559 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1560 framepos_t region_end = _region->position() + _region->length() - 1;
1562 if (end_frame > region_end) {
1563 _region->set_length (end_frame - _region->position(), this);
1566 _marked_for_selection.clear ();
1569 start_diff_command (_("step add"));
1570 diff_add_note (new_note, true, false);
1573 // last_step_edit_note = new_note;
1577 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1579 change_note_lengths (false, false, beats, false, true);
1583 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1585 assert(program.time >= 0);
1587 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1588 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1590 double height = midi_stream_view()->contents_height();
1592 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1593 new CanvasProgramChange(*this, *group,
1598 _custom_device_mode,
1599 program.time, program.channel, program.value));
1601 // Show unless program change is beyond the region bounds
1602 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1608 _pgm_changes.push_back(pgm_change);
1612 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1614 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1615 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1617 if (msb_control != 0) {
1618 msb = int(msb_control->get_double(true, time));
1621 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1622 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1624 if (lsb_control != 0) {
1625 lsb = lsb_control->get_double(true, time);
1628 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1629 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1630 double program_number = -1.0;
1631 if (program_control != 0) {
1632 program_number = program_control->get_double(true, time);
1635 key.msb = (int) floor(msb + 0.5);
1636 key.lsb = (int) floor(lsb + 0.5);
1637 key.program_number = (int) floor(program_number + 0.5);
1638 assert(key.is_sane());
1643 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1645 // TODO: Get the real event here and alter them at the original times
1646 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1647 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1648 if (msb_control != 0) {
1649 msb_control->set_double(double(new_patch.msb), true, old_program.time);
1652 // TODO: Get the real event here and alter them at the original times
1653 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1654 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1655 if (lsb_control != 0) {
1656 lsb_control->set_double(double(new_patch.lsb), true, old_program.time);
1659 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1660 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1662 assert(program_control != 0);
1663 program_control->set_double(float(new_patch.program_number), true, old_program.time);
1665 _pgm_changes.clear ();
1666 display_program_changes (); // XXX would be nice to limit to just old_program.channel
1670 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1672 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1673 alter_program_change(program_change_event, new_patch);
1677 MidiRegionView::previous_program(CanvasProgramChange& program)
1679 if (program.program() < 127) {
1680 MIDI::Name::PatchPrimaryKey key;
1681 get_patch_key_at(program.event_time(), program.channel(), key);
1682 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1684 key.program_number++;
1685 alter_program_change(program_change_event, key);
1690 MidiRegionView::next_program(CanvasProgramChange& program)
1692 if (program.program() > 0) {
1693 MIDI::Name::PatchPrimaryKey key;
1694 get_patch_key_at(program.event_time(), program.channel(), key);
1695 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1697 key.program_number--;
1698 alter_program_change(program_change_event, key);
1703 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1705 if (_selection.empty()) {
1709 if (_selection.erase (cne) > 0) {
1710 cerr << "Erased a CNE from selection\n";
1715 MidiRegionView::delete_selection()
1717 if (_selection.empty()) {
1721 start_diff_command (_("delete selection"));
1723 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1724 if ((*i)->selected()) {
1725 _diff_command->remove((*i)->note());
1735 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1737 start_diff_command (_("delete note"));
1738 _diff_command->remove (n);
1741 trackview.editor().hide_verbose_canvas_cursor ();
1745 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1747 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1748 if ((*i)->selected() && (*i) != ev) {
1749 (*i)->set_selected(false);
1750 (*i)->hide_velocity();
1758 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1760 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1763 Selection::iterator tmp = i;
1766 (*i)->set_selected (false);
1767 _selection.erase (i);
1776 /* don't bother with removing this regionview from the editor selection,
1777 since we're about to add another note, and thus put/keep this
1778 regionview in the editor selection.
1781 if (!ev->selected()) {
1782 add_to_selection (ev);
1787 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1789 uint8_t low_note = 127;
1790 uint8_t high_note = 0;
1791 MidiModel::Notes& notes (_model->notes());
1792 _optimization_iterator = _events.begin();
1798 if (extend && _selection.empty()) {
1804 /* scan existing selection to get note range */
1806 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1807 if ((*i)->note()->note() < low_note) {
1808 low_note = (*i)->note()->note();
1810 if ((*i)->note()->note() > high_note) {
1811 high_note = (*i)->note()->note();
1815 low_note = min (low_note, notenum);
1816 high_note = max (high_note, notenum);
1819 no_sound_notes = true;
1821 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1823 boost::shared_ptr<NoteType> note (*n);
1824 CanvasNoteEvent* cne;
1825 bool select = false;
1827 if (((1 << note->channel()) & channel_mask) != 0) {
1829 if ((note->note() >= low_note && note->note() <= high_note)) {
1832 } else if (note->note() == notenum) {
1838 if ((cne = find_canvas_note (note)) != 0) {
1839 // extend is false because we've taken care of it,
1840 // since it extends by time range, not pitch.
1841 note_selected (cne, add, false);
1845 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1849 no_sound_notes = false;
1853 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1855 MidiModel::Notes& notes (_model->notes());
1856 _optimization_iterator = _events.begin();
1858 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1860 boost::shared_ptr<NoteType> note (*n);
1861 CanvasNoteEvent* cne;
1863 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1864 if ((cne = find_canvas_note (note)) != 0) {
1865 if (cne->selected()) {
1866 note_deselected (cne);
1868 note_selected (cne, true, false);
1876 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1879 clear_selection_except(ev);
1884 if (!ev->selected()) {
1885 add_to_selection (ev);
1889 /* find end of latest note selected, select all between that and the start of "ev" */
1891 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1892 Evoral::MusicalTime latest = 0;
1894 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1895 if ((*i)->note()->end_time() > latest) {
1896 latest = (*i)->note()->end_time();
1898 if ((*i)->note()->time() < earliest) {
1899 earliest = (*i)->note()->time();
1903 if (ev->note()->end_time() > latest) {
1904 latest = ev->note()->end_time();
1907 if (ev->note()->time() < earliest) {
1908 earliest = ev->note()->time();
1911 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1913 /* find notes entirely within OR spanning the earliest..latest range */
1915 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1916 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1917 add_to_selection (*i);
1921 /* if events were guaranteed to be time sorted, we could do this.
1922 but as of sept 10th 2009, they no longer are.
1925 if ((*i)->note()->time() > latest) {
1934 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1936 remove_from_selection (ev);
1940 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1950 // TODO: Make this faster by storing the last updated selection rect, and only
1951 // adjusting things that are in the area that appears/disappeared.
1952 // We probably need a tree to be able to find events in O(log(n)) time.
1954 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1956 /* check if any corner of the note is inside the rect
1959 1) this is computing "touched by", not "contained by" the rect.
1960 2) this does not require that events be sorted in time.
1963 const double ix1 = (*i)->x1();
1964 const double ix2 = (*i)->x2();
1965 const double iy1 = (*i)->y1();
1966 const double iy2 = (*i)->y2();
1968 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1969 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1970 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1971 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1974 if (!(*i)->selected()) {
1975 add_to_selection (*i);
1977 } else if ((*i)->selected()) {
1978 // Not inside rectangle
1979 remove_from_selection (*i);
1985 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1987 Selection::iterator i = _selection.find (ev);
1989 if (i != _selection.end()) {
1990 _selection.erase (i);
1993 ev->set_selected (false);
1994 ev->hide_velocity ();
1996 if (_selection.empty()) {
1997 PublicEditor& editor (trackview.editor());
1998 editor.get_selection().remove (this);
2003 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2005 bool add_mrv_selection = false;
2007 if (_selection.empty()) {
2008 add_mrv_selection = true;
2011 if (_selection.insert (ev).second) {
2012 ev->set_selected (true);
2013 play_midi_note ((ev)->note());
2016 if (add_mrv_selection) {
2017 PublicEditor& editor (trackview.editor());
2018 editor.get_selection().add (this);
2023 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2025 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2026 PossibleChord to_play;
2027 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2029 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2030 if ((*i)->note()->time() < earliest) {
2031 earliest = (*i)->note()->time();
2035 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2036 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2037 to_play.push_back ((*i)->note());
2039 (*i)->move_event(dx, dy);
2042 if (dy && !_selection.empty() && !no_sound_notes && trackview.editor().sound_notes()) {
2044 if (to_play.size() > 1) {
2046 PossibleChord shifted;
2048 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2049 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2050 moved_note->set_note (moved_note->note() + cumulative_dy);
2051 shifted.push_back (moved_note);
2054 play_midi_chord (shifted);
2056 } else if (!to_play.empty()) {
2058 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2059 moved_note->set_note (moved_note->note() + cumulative_dy);
2060 play_midi_note (moved_note);
2066 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2068 assert (!_selection.empty());
2070 uint8_t lowest_note_in_selection = 127;
2071 uint8_t highest_note_in_selection = 0;
2072 uint8_t highest_note_difference = 0;
2074 // find highest and lowest notes first
2076 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2077 uint8_t pitch = (*i)->note()->note();
2078 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2079 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2083 cerr << "dnote: " << (int) dnote << endl;
2084 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2085 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2086 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2087 << int(highest_note_in_selection) << endl;
2088 cerr << "selection size: " << _selection.size() << endl;
2089 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2092 // Make sure the note pitch does not exceed the MIDI standard range
2093 if (highest_note_in_selection + dnote > 127) {
2094 highest_note_difference = highest_note_in_selection - 127;
2097 start_diff_command(_("move notes"));
2099 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2101 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2107 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
2109 uint8_t original_pitch = (*i)->note()->note();
2110 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2112 // keep notes in standard midi range
2113 clamp_to_0_127(new_pitch);
2115 // keep original pitch if note is dragged outside valid midi range
2116 if ((original_pitch != 0 && new_pitch == 0)
2117 || (original_pitch != 127 && new_pitch == 127)) {
2118 new_pitch = original_pitch;
2121 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2122 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2124 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
2129 // care about notes being moved beyond the upper/lower bounds on the canvas
2130 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2131 highest_note_in_selection > midi_stream_view()->highest_note()) {
2132 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2137 MidiRegionView::snap_pixel_to_frame(double x)
2139 PublicEditor& editor = trackview.editor();
2140 // x is region relative, convert it to global absolute frames
2141 framepos_t frame = editor.pixel_to_frame(x) + _region->position();
2142 editor.snap_to(frame);
2143 return frame - _region->position(); // convert back to region relative
2147 MidiRegionView::snap_frame_to_frame(framepos_t x)
2149 PublicEditor& editor = trackview.editor();
2150 // x is region relative, convert it to global absolute frames
2151 framepos_t frame = x + _region->position();
2152 editor.snap_to(frame);
2153 return frame - _region->position(); // convert back to region relative
2157 MidiRegionView::snap_to_pixel(double x)
2159 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2163 MidiRegionView::get_position_pixels()
2165 framepos_t region_frame = get_position();
2166 return trackview.editor().frame_to_pixel(region_frame);
2170 MidiRegionView::get_end_position_pixels()
2172 framepos_t frame = get_position() + get_duration ();
2173 return trackview.editor().frame_to_pixel(frame);
2177 MidiRegionView::beats_to_frames(double beats) const
2179 return _time_converter.to(beats);
2183 MidiRegionView::frames_to_beats(framepos_t frames) const
2185 return _time_converter.from(frames);
2189 MidiRegionView::begin_resizing (bool /*at_front*/)
2191 _resize_data.clear();
2193 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2194 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2196 // only insert CanvasNotes into the map
2198 NoteResizeData *resize_data = new NoteResizeData();
2199 resize_data->canvas_note = note;
2201 // create a new SimpleRect from the note which will be the resize preview
2202 SimpleRect *resize_rect = new SimpleRect(
2203 *group, note->x1(), note->y1(), note->x2(), note->y2());
2205 // calculate the colors: get the color settings
2206 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2207 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2210 // make the resize preview notes more transparent and bright
2211 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2213 // calculate color based on note velocity
2214 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2215 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2219 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2220 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2222 resize_data->resize_rect = resize_rect;
2223 _resize_data.push_back(resize_data);
2228 /** Update resizing notes while user drags.
2229 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2230 * @param at_front which end of the note (true == note on, false == note off)
2231 * @param delta_x change in mouse position since the start of the drag
2232 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2233 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2234 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2235 * as the \a primary note.
2238 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2240 bool cursor_set = false;
2242 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2243 SimpleRect* resize_rect = (*i)->resize_rect;
2244 CanvasNote* canvas_note = (*i)->canvas_note;
2249 current_x = canvas_note->x1() + delta_x;
2251 current_x = primary->x1() + delta_x;
2255 current_x = canvas_note->x2() + delta_x;
2257 current_x = primary->x2() + delta_x;
2262 resize_rect->property_x1() = snap_to_pixel(current_x);
2263 resize_rect->property_x2() = canvas_note->x2();
2265 resize_rect->property_x2() = snap_to_pixel(current_x);
2266 resize_rect->property_x1() = canvas_note->x1();
2272 beats = snap_pixel_to_frame (current_x);
2273 beats = frames_to_beats (beats);
2278 if (beats < canvas_note->note()->end_time()) {
2279 len = canvas_note->note()->time() - beats;
2280 len += canvas_note->note()->length();
2285 if (beats >= canvas_note->note()->time()) {
2286 len = beats - canvas_note->note()->time();
2293 snprintf (buf, sizeof (buf), "%.3g beats", len);
2294 trackview.editor().show_verbose_canvas_cursor_with (buf);
2303 /** Finish resizing notes when the user releases the mouse button.
2304 * Parameters the same as for \a update_resizing().
2307 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2309 start_diff_command(_("resize notes"));
2311 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2312 CanvasNote* canvas_note = (*i)->canvas_note;
2313 SimpleRect* resize_rect = (*i)->resize_rect;
2318 current_x = canvas_note->x1() + delta_x;
2320 current_x = primary->x1() + delta_x;
2324 current_x = canvas_note->x2() + delta_x;
2326 current_x = primary->x2() + delta_x;
2330 current_x = snap_pixel_to_frame (current_x);
2331 current_x = frames_to_beats (current_x);
2333 if (at_front && current_x < canvas_note->note()->end_time()) {
2334 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2336 double len = canvas_note->note()->time() - current_x;
2337 len += canvas_note->note()->length();
2340 /* XXX convert to beats */
2341 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2346 double len = current_x - canvas_note->note()->time();
2349 /* XXX convert to beats */
2350 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2358 _resize_data.clear();
2363 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2365 diff_add_change (event, MidiModel::DiffCommand::Channel, (uint8_t) channel);
2369 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2371 uint8_t new_velocity;
2374 new_velocity = event->note()->velocity() + velocity;
2375 clamp_to_0_127(new_velocity);
2377 new_velocity = velocity;
2380 event->set_selected (event->selected()); // change color
2382 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2386 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2391 new_note = event->note()->note() + note;
2396 clamp_to_0_127 (new_note);
2397 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2401 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2403 bool change_start = false;
2404 bool change_length = false;
2405 Evoral::MusicalTime new_start = 0;
2406 Evoral::MusicalTime new_length = 0;
2408 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2410 front_delta: if positive - move the start of the note later in time (shortening it)
2411 if negative - move the start of the note earlier in time (lengthening it)
2413 end_delta: if positive - move the end of the note later in time (lengthening it)
2414 if negative - move the end of the note earlier in time (shortening it)
2418 if (front_delta < 0) {
2420 if (event->note()->time() < -front_delta) {
2423 new_start = event->note()->time() + front_delta; // moves earlier
2426 /* start moved toward zero, so move the end point out to where it used to be.
2427 Note that front_delta is negative, so this increases the length.
2430 new_length = event->note()->length() - front_delta;
2431 change_start = true;
2432 change_length = true;
2436 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2438 if (new_pos < event->note()->end_time()) {
2439 new_start = event->note()->time() + front_delta;
2440 /* start moved toward the end, so move the end point back to where it used to be */
2441 new_length = event->note()->length() - front_delta;
2442 change_start = true;
2443 change_length = true;
2450 bool can_change = true;
2451 if (end_delta < 0) {
2452 if (event->note()->length() < -end_delta) {
2458 new_length = event->note()->length() + end_delta;
2459 change_length = true;
2464 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2467 if (change_length) {
2468 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2473 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2475 Evoral::MusicalTime new_time;
2479 if (event->note()->time() < -delta) {
2482 new_time = event->note()->time() + delta;
2485 new_time = event->note()->time() + delta;
2491 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2495 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2497 diff_add_change (event, MidiModel::DiffCommand::Length, t);
2501 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2505 if (_selection.empty()) {
2520 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2521 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2527 start_diff_command(_("change velocities"));
2529 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2530 Selection::iterator next = i;
2532 change_note_velocity (*i, delta, true);
2538 if (!_selection.empty()) {
2540 snprintf (buf, sizeof (buf), "Vel %d",
2541 (int) (*_selection.begin())->note()->velocity());
2542 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 10);
2548 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2550 if (_selection.empty()) {
2567 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2569 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2573 if ((int8_t) (*i)->note()->note() + delta > 127) {
2580 start_diff_command (_("transpose"));
2582 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2583 Selection::iterator next = i;
2585 change_note_note (*i, delta, true);
2593 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2599 /* grab the current grid distance */
2601 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2603 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2604 cerr << "Grid type not available as beats - TO BE FIXED\n";
2614 start_diff_command (_("change note lengths"));
2616 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2617 Selection::iterator next = i;
2620 /* note the negation of the delta for start */
2622 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2631 MidiRegionView::nudge_notes (bool forward)
2633 if (_selection.empty()) {
2637 /* pick a note as the point along the timeline to get the nudge distance.
2638 its not necessarily the earliest note, so we may want to pull the notes out
2639 into a vector and sort before using the first one.
2642 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2644 framepos_t distance;
2646 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2648 /* grid is off - use nudge distance */
2650 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2656 framepos_t next_pos = ref_point;
2659 if (max_framepos - 1 < next_pos) {
2663 if (next_pos == 0) {
2669 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2670 distance = ref_point - next_pos;
2673 if (distance == 0) {
2677 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2683 start_diff_command (_("nudge"));
2685 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2686 Selection::iterator next = i;
2688 change_note_time (*i, delta, true);
2696 MidiRegionView::change_channel(uint8_t channel)
2698 start_diff_command(_("change channel"));
2699 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2700 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2708 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2710 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2712 pre_enter_cursor = editor->get_canvas_cursor ();
2714 if (_mouse_state == SelectTouchDragging) {
2715 note_selected (ev, true);
2718 show_verbose_canvas_cursor (ev->note ());
2722 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2724 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2726 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2727 (*i)->hide_velocity ();
2730 editor->hide_verbose_canvas_cursor ();
2732 if (pre_enter_cursor) {
2733 editor->set_canvas_cursor (pre_enter_cursor);
2734 pre_enter_cursor = 0;
2739 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
2741 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2743 if (x_fraction > 0.0 && x_fraction < 0.25) {
2744 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
2745 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
2746 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
2748 if (pre_enter_cursor && can_set_cursor) {
2749 editor->set_canvas_cursor (pre_enter_cursor);
2755 MidiRegionView::set_frame_color()
2758 if (_selected && should_show_selection) {
2759 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2761 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2767 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2771 case FilterChannels:
2772 _force_channel = -1;
2775 _force_channel = mask;
2776 mask = 0xFFFF; // Show all notes as active (below)
2779 // Update notes for selection
2780 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2781 (*i)->on_channel_selection_change(mask);
2784 _last_channel_selection = mask;
2788 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2790 _model_name = model;
2791 _custom_device_mode = custom_device_mode;
2796 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2798 if (_selection.empty()) {
2802 PublicEditor& editor (trackview.editor());
2807 editor.get_cut_buffer().add (selection_as_cut_buffer());
2815 start_diff_command();
2817 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2823 diff_remove_note (*i);
2833 MidiRegionView::selection_as_cut_buffer () const
2837 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2838 NoteType* n = (*i)->note().get();
2839 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2842 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2849 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
2855 start_diff_command (_("paste"));
2857 Evoral::MusicalTime beat_delta;
2858 Evoral::MusicalTime paste_pos_beats;
2859 Evoral::MusicalTime duration;
2860 Evoral::MusicalTime end_point = 0;
2862 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2863 paste_pos_beats = frames_to_beats (pos - _region->position());
2864 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2865 paste_pos_beats = 0;
2869 for (int n = 0; n < (int) times; ++n) {
2871 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2873 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2874 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2876 /* make all newly added notes selected */
2878 diff_add_note (copied_note, true);
2879 end_point = copied_note->end_time();
2882 paste_pos_beats += duration;
2885 /* if we pasted past the current end of the region, extend the region */
2887 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
2888 framepos_t region_end = _region->position() + _region->length() - 1;
2890 if (end_frame > region_end) {
2892 trackview.session()->begin_reversible_command (_("paste"));
2894 _region->clear_changes ();
2895 _region->set_length (end_frame, this);
2896 trackview.session()->add_command (new StatefulDiffCommand (_region));
2902 struct EventNoteTimeEarlyFirstComparator {
2903 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2904 return a->note()->time() < b->note()->time();
2909 MidiRegionView::time_sort_events ()
2911 if (!_sort_needed) {
2915 EventNoteTimeEarlyFirstComparator cmp;
2918 _sort_needed = false;
2922 MidiRegionView::goto_next_note ()
2924 // framepos_t pos = -1;
2925 bool use_next = false;
2927 if (_events.back()->selected()) {
2931 time_sort_events ();
2933 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2934 if ((*i)->selected()) {
2937 } else if (use_next) {
2939 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2944 /* use the first one */
2946 unique_select (_events.front());
2951 MidiRegionView::goto_previous_note ()
2953 // framepos_t pos = -1;
2954 bool use_next = false;
2956 if (_events.front()->selected()) {
2960 time_sort_events ();
2962 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2963 if ((*i)->selected()) {
2966 } else if (use_next) {
2968 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2973 /* use the last one */
2975 unique_select (*(_events.rbegin()));
2979 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2981 bool had_selected = false;
2983 time_sort_events ();
2985 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2986 if ((*i)->selected()) {
2987 selected.insert ((*i)->note());
2988 had_selected = true;
2992 if (allow_all_if_none_selected && !had_selected) {
2993 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2994 selected.insert ((*i)->note());
3000 MidiRegionView::update_ghost_note (double x, double y)
3006 framepos_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
3007 trackview.editor().snap_to (f);
3008 f -= _region->position ();
3011 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
3016 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
3018 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
3019 _ghost_note->note()->set_length (length);
3020 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3022 update_note (_ghost_note);
3024 show_verbose_canvas_cursor (_ghost_note->note ());
3028 MidiRegionView::create_ghost_note (double x, double y)
3033 boost::shared_ptr<NoteType> g (new NoteType);
3034 _ghost_note = new NoEventCanvasNote (*this, *group, g);
3035 update_ghost_note (x, y);
3036 _ghost_note->show ();
3041 show_verbose_canvas_cursor (_ghost_note->note ());
3045 MidiRegionView::snap_changed ()
3051 create_ghost_note (_last_ghost_x, _last_ghost_y);
3055 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
3058 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
3059 Evoral::midi_note_name (n->note()).c_str(),
3061 (int) n->velocity());
3062 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 20);
3066 MidiRegionView::drop_down_keys ()
3068 _mouse_state = None;
3072 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3074 double note = midi_stream_view()->y_to_note(y);
3076 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3078 cerr << "Selecting by position\n";
3080 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3082 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3083 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3084 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3085 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3090 bool add_mrv_selection = false;
3092 if (_selection.empty()) {
3093 add_mrv_selection = true;
3096 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3097 if (_selection.insert (*i).second) {
3098 (*i)->set_selected (true);
3102 if (add_mrv_selection) {
3103 PublicEditor& editor (trackview.editor());
3104 editor.get_selection().add (this);
3109 MidiRegionView::color_handler ()
3111 RegionView::color_handler ();
3113 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3114 (*i)->set_selected ((*i)->selected()); // will change color
3117 /* XXX probably more to do here */
3121 MidiRegionView::enable_display (bool yn)
3123 RegionView::enable_display (yn);
3130 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3132 if (_step_edit_cursor == 0) {
3133 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3135 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3136 _step_edit_cursor->property_y1() = 0;
3137 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3138 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3139 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3142 move_step_edit_cursor (pos);
3143 _step_edit_cursor->show ();
3147 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3149 _step_edit_cursor_position = pos;
3151 if (_step_edit_cursor) {
3152 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3153 _step_edit_cursor->property_x1() = pixel;
3154 set_step_edit_cursor_width (_step_edit_cursor_width);
3159 MidiRegionView::hide_step_edit_cursor ()
3161 if (_step_edit_cursor) {
3162 _step_edit_cursor->hide ();
3167 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3169 _step_edit_cursor_width = beats;
3171 if (_step_edit_cursor) {
3172 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
3176 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3177 * @param buf Data that has been recorded.
3178 * @param w Source that this data will end up in.
3181 MidiRegionView::data_recorded (boost::shared_ptr<MidiBuffer> buf, boost::weak_ptr<MidiSource> w)
3183 if (!_active_notes) {
3184 /* we aren't actively being recorded to */
3188 boost::shared_ptr<MidiSource> src = w.lock ();
3189 if (!src || src != midi_region()->midi_source()) {
3190 /* recorded data was not destined for our source */
3194 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3195 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3197 framepos_t back = max_framepos;
3199 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3200 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3201 assert (ev.buffer ());
3203 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3205 if (ev.type() == MIDI_CMD_NOTE_ON) {
3207 boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > note (
3208 new Evoral::Note<Evoral::MusicalTime> (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3211 add_note (note, true);
3213 /* fix up our note range */
3214 if (ev.note() < _current_range_min) {
3215 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3216 } else if (ev.note() > _current_range_max) {
3217 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3220 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3221 resolve_note (ev.note (), time_beats);
3227 midi_stream_view()->check_record_layers (region(), back);