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(*group))
91 , _note_diff_command (0)
94 , _step_edit_cursor (0)
95 , _step_edit_cursor_width (1.0)
96 , _step_edit_cursor_position (0.0)
97 , _temporary_note_group (0)
100 , _sort_needed (true)
101 , _optimization_iterator (_events.end())
103 , no_sound_notes (false)
106 , pre_enter_cursor (0)
108 _note_group->raise_to_top();
109 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
111 connect_to_diskstream ();
114 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
115 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
116 TimeAxisViewItem::Visibility visibility)
117 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
119 , _last_channel_selection(0xFFFF)
120 , _model_name(string())
121 , _custom_device_mode(string())
123 , _note_group(new ArdourCanvas::Group(*parent))
124 , _note_diff_command (0)
127 , _step_edit_cursor (0)
128 , _step_edit_cursor_width (1.0)
129 , _step_edit_cursor_position (0.0)
130 , _temporary_note_group (0)
133 , _sort_needed (true)
134 , _optimization_iterator (_events.end())
136 , no_sound_notes (false)
140 _note_group->raise_to_top();
141 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
143 connect_to_diskstream ();
146 MidiRegionView::MidiRegionView (const MidiRegionView& other)
147 : sigc::trackable(other)
150 , _last_channel_selection(0xFFFF)
151 , _model_name(string())
152 , _custom_device_mode(string())
154 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
155 , _note_diff_command (0)
158 , _step_edit_cursor (0)
159 , _step_edit_cursor_width (1.0)
160 , _step_edit_cursor_position (0.0)
161 , _temporary_note_group (0)
164 , _sort_needed (true)
165 , _optimization_iterator (_events.end())
167 , no_sound_notes (false)
174 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
175 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
180 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
181 : RegionView (other, boost::shared_ptr<Region> (region))
183 , _last_channel_selection(0xFFFF)
184 , _model_name(string())
185 , _custom_device_mode(string())
187 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
188 , _note_diff_command (0)
191 , _step_edit_cursor (0)
192 , _step_edit_cursor_width (1.0)
193 , _step_edit_cursor_position (0.0)
194 , _temporary_note_group (0)
197 , _sort_needed (true)
198 , _optimization_iterator (_events.end())
200 , no_sound_notes (false)
207 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
208 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
214 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
216 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
218 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
219 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
223 midi_region()->midi_source(0)->load_model();
226 _model = midi_region()->midi_source(0)->model();
227 _enable_display = false;
229 RegionView::init (basic_color, false);
231 compute_colors (basic_color);
233 set_height (trackview.current_height());
236 region_sync_changed ();
237 region_resized (ARDOUR::bounds_change);
240 reset_width_dependent_items (_pixel_width);
244 _enable_display = true;
247 display_model (_model);
251 group->raise_to_top();
252 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
254 midi_view()->signal_channel_mode_changed().connect(
255 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
257 midi_view()->signal_midi_patch_settings_changed().connect(
258 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
260 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
262 connect_to_diskstream ();
266 MidiRegionView::connect_to_diskstream ()
268 midi_view()->midi_track()->DataRecorded.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::data_recorded, this, _1, _2), gui_context ());
272 MidiRegionView::canvas_event(GdkEvent* ev)
275 case GDK_ENTER_NOTIFY:
276 case GDK_LEAVE_NOTIFY:
277 _last_event_x = ev->crossing.x;
278 _last_event_y = ev->crossing.y;
280 case GDK_MOTION_NOTIFY:
281 _last_event_x = ev->motion.x;
282 _last_event_y = ev->motion.y;
288 if (!trackview.editor().internal_editing()) {
292 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
293 to its items, which means that ev->type == GDK_SCROLL will never be seen
298 return scroll (&ev->scroll);
301 return key_press (&ev->key);
303 case GDK_KEY_RELEASE:
304 return key_release (&ev->key);
306 case GDK_BUTTON_PRESS:
307 return button_press (&ev->button);
309 case GDK_2BUTTON_PRESS:
312 case GDK_BUTTON_RELEASE:
313 return button_release (&ev->button);
315 case GDK_ENTER_NOTIFY:
316 return enter_notify (&ev->crossing);
318 case GDK_LEAVE_NOTIFY:
319 return leave_notify (&ev->crossing);
321 case GDK_MOTION_NOTIFY:
322 return motion (&ev->motion);
332 MidiRegionView::remove_ghost_note ()
339 MidiRegionView::enter_notify (GdkEventCrossing* ev)
341 trackview.editor().MouseModeChanged.connect (
342 _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
345 Keyboard::magic_widget_grab_focus();
348 if (trackview.editor().current_mouse_mode() == MouseRange) {
349 create_ghost_note (ev->x, ev->y);
356 MidiRegionView::leave_notify (GdkEventCrossing*)
358 _mouse_mode_connection.disconnect ();
360 trackview.editor().hide_verbose_canvas_cursor ();
361 remove_ghost_note ();
366 MidiRegionView::mouse_mode_changed ()
368 if (trackview.editor().current_mouse_mode() == MouseRange && trackview.editor().internal_editing()) {
369 create_ghost_note (_last_event_x, _last_event_y);
371 remove_ghost_note ();
372 trackview.editor().hide_verbose_canvas_cursor ();
377 MidiRegionView::button_press (GdkEventButton* ev)
381 group->w2i (_last_x, _last_y);
383 if (_mouse_state != SelectTouchDragging && ev->button == 1) {
384 _pressed_button = ev->button;
385 _mouse_state = Pressed;
389 _pressed_button = ev->button;
395 MidiRegionView::button_release (GdkEventButton* ev)
397 double event_x, event_y;
398 framepos_t event_frame = 0;
402 group->w2i(event_x, event_y);
403 group->ungrab(ev->time);
404 event_frame = trackview.editor().pixel_to_frame(event_x);
406 if (ev->button == 3) {
408 } else if (_pressed_button != 1) {
412 switch (_mouse_state) {
413 case Pressed: // Clicked
414 switch (trackview.editor().current_mouse_mode()) {
418 maybe_select_by_position (ev, event_x, event_y);
424 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
428 create_note_at (event_x, event_y, beats, true);
436 case SelectRectDragging: // Select drag done
442 case AddDragging: // Add drag done
444 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
445 const double x = _drag_rect->property_x1();
446 const double length = trackview.editor().pixel_to_frame
447 (_drag_rect->property_x2() - _drag_rect->property_x1());
449 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), true);
455 create_ghost_note (ev->x, ev->y);
465 MidiRegionView::motion (GdkEventMotion* ev)
467 double event_x, event_y;
468 framepos_t event_frame = 0;
472 group->w2i(event_x, event_y);
474 // convert event_x to global frame
475 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
476 trackview.editor().snap_to(event_frame);
477 // convert event_frame back to local coordinates relative to position
478 event_frame -= _region->position();
481 update_ghost_note (ev->x, ev->y);
484 /* any motion immediately hides velocity text that may have been visible */
486 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
487 (*i)->hide_velocity ();
490 switch (_mouse_state) {
491 case Pressed: // Maybe start a drag, if we've moved a bit
493 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
494 /* no appreciable movement since the button was pressed */
499 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
500 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
501 Gdk::Cursor(Gdk::FLEUR), ev->time);
504 _drag_start_x = event_x;
505 _drag_start_y = event_y;
507 _drag_rect = new ArdourCanvas::SimpleRect(*group);
508 _drag_rect->property_x1() = event_x;
509 _drag_rect->property_y1() = event_y;
510 _drag_rect->property_x2() = event_x;
511 _drag_rect->property_y2() = event_y;
512 _drag_rect->property_outline_what() = 0xFF;
513 _drag_rect->property_outline_color_rgba()
514 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
515 _drag_rect->property_fill_color_rgba()
516 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
518 _mouse_state = SelectRectDragging;
521 // Add note drag start
522 } else if (trackview.editor().internal_editing()) {
527 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
528 Gdk::Cursor(Gdk::FLEUR), ev->time);
531 _drag_start_x = event_x;
532 _drag_start_y = event_y;
534 _drag_rect = new ArdourCanvas::SimpleRect(*group);
535 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
537 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
538 midi_stream_view()->y_to_note(event_y));
539 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
540 _drag_rect->property_y2() = _drag_rect->property_y1()
541 + floor(midi_stream_view()->note_height());
542 _drag_rect->property_outline_what() = 0xFF;
543 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
544 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
546 _mouse_state = AddDragging;
552 case SelectRectDragging: // Select drag motion
553 case AddDragging: // Add note drag motion
557 GdkModifierType state;
558 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
563 if (_mouse_state == AddDragging)
564 event_x = trackview.editor().frame_to_pixel(event_frame);
567 if (event_x > _drag_start_x)
568 _drag_rect->property_x2() = event_x;
570 _drag_rect->property_x1() = event_x;
573 if (_drag_rect && _mouse_state == SelectRectDragging) {
574 if (event_y > _drag_start_y)
575 _drag_rect->property_y2() = event_y;
577 _drag_rect->property_y1() = event_y;
579 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
585 case SelectTouchDragging:
597 MidiRegionView::scroll (GdkEventScroll* ev)
599 if (_selection.empty()) {
603 trackview.editor().hide_verbose_canvas_cursor ();
605 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
607 if (ev->direction == GDK_SCROLL_UP) {
608 change_velocities (true, fine, false);
609 } else if (ev->direction == GDK_SCROLL_DOWN) {
610 change_velocities (false, fine, false);
616 MidiRegionView::key_press (GdkEventKey* ev)
618 /* since GTK bindings are generally activated on press, and since
619 detectable auto-repeat is the name of the game and only sends
620 repeated presses, carry out key actions at key press, not release.
623 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
624 _mouse_state = SelectTouchDragging;
627 } else if (ev->keyval == GDK_Escape) {
631 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
633 bool start = (ev->keyval == GDK_comma);
634 bool end = (ev->keyval == GDK_period);
635 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
636 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
638 change_note_lengths (fine, shorter, 0.0, start, end);
642 } else if (ev->keyval == GDK_Delete) {
647 } else if (ev->keyval == GDK_Tab) {
649 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
650 goto_previous_note ();
656 } else if (ev->keyval == GDK_Up) {
658 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
659 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
661 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
662 change_velocities (true, fine, allow_smush);
664 transpose (true, fine, allow_smush);
668 } else if (ev->keyval == GDK_Down) {
670 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
671 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
673 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
674 change_velocities (false, fine, allow_smush);
676 transpose (false, fine, allow_smush);
680 } else if (ev->keyval == GDK_Left) {
685 } else if (ev->keyval == GDK_Right) {
690 } else if (ev->keyval == GDK_Control_L) {
699 MidiRegionView::key_release (GdkEventKey* ev)
701 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
709 MidiRegionView::show_list_editor ()
712 _list_editor = new MidiListEditor (trackview.session(), midi_region());
714 _list_editor->present ();
717 /** Add a note to the model, and the view, at a canvas (click) coordinate.
718 * \param x horizontal position in pixels
719 * \param y vertical position in pixels
720 * \param length duration of the note in beats, which will be snapped to the grid
721 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
724 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
726 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
727 MidiStreamView* const view = mtv->midi_view();
729 double note = midi_stream_view()->y_to_note(y);
732 assert(note <= 127.0);
734 // Start of note in frames relative to region start
735 framepos_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
736 assert(start_frames >= 0);
739 length = frames_to_beats(
740 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
742 assert (length != 0);
745 length = frames_to_beats (beats_to_frames (length) - 1);
748 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
752 /* pick the highest selected channel, unless all channels are selected,
753 which is interpreted to mean channel 1 (zero)
756 for (uint16_t i = 0; i < 16; ++i) {
757 if (chn_mask & (1<<i)) {
767 const boost::shared_ptr<NoteType> new_note (new NoteType (channel,
768 frames_to_beats(start_frames + _region->start()), length,
769 (uint8_t)note, 0x40));
771 if (_model->contains (new_note)) {
775 view->update_note_range(new_note->note());
777 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note");
779 _model->apply_command(*trackview.session(), cmd);
781 play_midi_note (new_note);
785 MidiRegionView::clear_events()
790 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
791 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
796 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
801 _pgm_changes.clear();
803 _optimization_iterator = _events.end();
807 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
811 content_connection.disconnect ();
812 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
816 if (_enable_display) {
822 MidiRegionView::start_note_diff_command (string name)
824 if (!_note_diff_command) {
825 _note_diff_command = _model->new_note_diff_command (name);
830 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
832 if (_note_diff_command) {
833 _note_diff_command->add (note);
836 _marked_for_selection.insert(note);
839 _marked_for_velocity.insert(note);
844 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
846 if (_note_diff_command && ev->note()) {
847 _note_diff_command->remove(ev->note());
852 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
853 MidiModel::NoteDiffCommand::Property property,
856 if (_note_diff_command) {
857 _note_diff_command->change (ev->note(), property, val);
862 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
863 MidiModel::NoteDiffCommand::Property property,
864 Evoral::MusicalTime val)
866 if (_note_diff_command) {
867 _note_diff_command->change (ev->note(), property, val);
872 MidiRegionView::apply_diff ()
876 if (!_note_diff_command) {
880 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
881 // Mark all selected notes for selection when model reloads
882 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
883 _marked_for_selection.insert((*i)->note());
887 _model->apply_command(*trackview.session(), _note_diff_command);
888 _note_diff_command = 0;
889 midi_view()->midi_track()->playlist_modified();
892 _marked_for_selection.clear();
895 _marked_for_velocity.clear();
899 MidiRegionView::apply_diff_as_subcommand ()
903 if (!_note_diff_command) {
907 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
908 // Mark all selected notes for selection when model reloads
909 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
910 _marked_for_selection.insert((*i)->note());
914 _model->apply_command_as_subcommand(*trackview.session(), _note_diff_command);
915 _note_diff_command = 0;
916 midi_view()->midi_track()->playlist_modified();
919 _marked_for_selection.clear();
921 _marked_for_velocity.clear();
926 MidiRegionView::abort_command()
928 delete _note_diff_command;
929 _note_diff_command = 0;
934 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
936 if (_optimization_iterator != _events.end()) {
937 ++_optimization_iterator;
940 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
941 return *_optimization_iterator;
944 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
945 if ((*_optimization_iterator)->note() == note) {
946 return *_optimization_iterator;
954 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
956 MidiModel::Notes notes;
957 _model->get_notes (notes, op, val, chan_mask);
959 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
960 CanvasNoteEvent* cne = find_canvas_note (*n);
968 MidiRegionView::redisplay_model()
970 // Don't redisplay the model if we're currently recording and displaying that
976 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
980 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
984 MidiModel::ReadLock lock(_model->read_lock());
986 MidiModel::Notes& notes (_model->notes());
987 _optimization_iterator = _events.begin();
989 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
991 boost::shared_ptr<NoteType> note (*n);
992 CanvasNoteEvent* cne;
995 if (note_in_region_range (note, visible)) {
997 if ((cne = find_canvas_note (note)) != 0) {
1004 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1006 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1018 add_note (note, visible);
1023 if ((cne = find_canvas_note (note)) != 0) {
1031 /* remove note items that are no longer valid */
1033 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1034 if (!(*i)->valid ()) {
1036 i = _events.erase (i);
1042 _pgm_changes.clear();
1046 display_program_changes();
1048 _marked_for_selection.clear ();
1049 _marked_for_velocity.clear ();
1051 /* we may have caused _events to contain things out of order (e.g. if a note
1052 moved earlier or later). we don't generally need them in time order, but
1053 make a note that a sort is required for those cases that require it.
1056 _sort_needed = true;
1060 MidiRegionView::display_program_changes()
1062 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1063 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1065 for (uint8_t i = 0; i < 16; ++i) {
1066 if (chn_mask & (1<<i)) {
1067 display_program_changes_on_channel (i);
1073 MidiRegionView::display_program_changes_on_channel(uint8_t channel)
1075 boost::shared_ptr<Evoral::Control> control =
1076 _model->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation, channel));
1082 Glib::Mutex::Lock lock (control->list()->lock());
1084 for (AutomationList::const_iterator event = control->list()->begin();
1085 event != control->list()->end(); ++event) {
1086 double event_time = (*event)->when;
1087 double program_number = floor((*event)->value + 0.5);
1089 // Get current value of bank select MSB at time of the program change
1090 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1091 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1093 if (msb_control != 0) {
1094 msb = uint8_t(floor(msb_control->get_double(true, event_time) + 0.5));
1097 // Get current value of bank select LSB at time of the program change
1098 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1099 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1101 if (lsb_control != 0) {
1102 lsb = uint8_t(floor(lsb_control->get_double(true, event_time) + 0.5));
1105 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
1107 boost::shared_ptr<MIDI::Name::Patch> patch =
1108 MIDI::Name::MidiPatchManager::instance().find_patch(
1109 _model_name, _custom_device_mode, channel, patch_key);
1111 PCEvent program_change(event_time, uint8_t(program_number), channel);
1114 add_canvas_program_change (program_change, patch->name());
1117 // program_number is zero-based: convert to one-based
1118 snprintf(buf, 4, "%d", int(program_number+1));
1119 add_canvas_program_change (program_change, buf);
1125 MidiRegionView::display_sysexes()
1127 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1128 Evoral::MusicalTime time = (*i)->time();
1133 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1134 str << int((*i)->buffer()[b]);
1135 if (b != (*i)->size() -1) {
1139 string text = str.str();
1141 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1143 double height = midi_stream_view()->contents_height();
1145 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1146 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1148 // Show unless program change is beyond the region bounds
1149 if (time - _region->start() >= _region->length() || time < _region->start()) {
1155 _sys_exes.push_back(sysex);
1160 MidiRegionView::~MidiRegionView ()
1162 in_destructor = true;
1164 trackview.editor().hide_verbose_canvas_cursor ();
1166 note_delete_connection.disconnect ();
1168 delete _list_editor;
1170 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1172 if (_active_notes) {
1180 delete _note_diff_command;
1181 delete _step_edit_cursor;
1182 delete _temporary_note_group;
1186 MidiRegionView::region_resized (const PropertyChange& what_changed)
1188 RegionView::region_resized(what_changed);
1190 if (what_changed.contains (ARDOUR::Properties::position)) {
1191 set_duration(_region->length(), 0);
1192 if (_enable_display) {
1199 MidiRegionView::reset_width_dependent_items (double pixel_width)
1201 RegionView::reset_width_dependent_items(pixel_width);
1202 assert(_pixel_width == pixel_width);
1204 if (_enable_display) {
1208 move_step_edit_cursor (_step_edit_cursor_position);
1209 set_step_edit_cursor_width (_step_edit_cursor_width);
1213 MidiRegionView::set_height (double height)
1215 static const double FUDGE = 2.0;
1216 const double old_height = _height;
1217 RegionView::set_height(height);
1218 _height = height - FUDGE;
1220 apply_note_range(midi_stream_view()->lowest_note(),
1221 midi_stream_view()->highest_note(),
1222 height != old_height + FUDGE);
1225 name_pixbuf->raise_to_top();
1228 for (PgmChanges::iterator x = _pgm_changes.begin(); x != _pgm_changes.end(); ++x) {
1229 (*x)->set_height (midi_stream_view()->contents_height());
1232 if (_step_edit_cursor) {
1233 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1238 /** Apply the current note range from the stream view
1239 * by repositioning/hiding notes as necessary
1242 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1244 if (!_enable_display) {
1248 if (!force && _current_range_min == min && _current_range_max == max) {
1252 _current_range_min = min;
1253 _current_range_max = max;
1255 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1256 CanvasNoteEvent* event = *i;
1257 boost::shared_ptr<NoteType> note (event->note());
1259 if (note->note() < _current_range_min ||
1260 note->note() > _current_range_max) {
1266 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1268 const double y1 = midi_stream_view()->note_to_y(note->note());
1269 const double y2 = y1 + floor(midi_stream_view()->note_height());
1271 cnote->property_y1() = y1;
1272 cnote->property_y2() = y2;
1274 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1276 const double diamond_size = update_hit (chit);
1278 chit->set_height (diamond_size);
1284 MidiRegionView::add_ghost (TimeAxisView& tv)
1288 double unit_position = _region->position () / samples_per_unit;
1289 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1290 MidiGhostRegion* ghost;
1292 if (mtv && mtv->midi_view()) {
1293 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1294 to allow having midi notes on top of note lines and waveforms.
1296 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1298 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1301 ghost->set_height ();
1302 ghost->set_duration (_region->length() / samples_per_unit);
1303 ghosts.push_back (ghost);
1305 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1306 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1307 ghost->add_note(note);
1311 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1317 /** Begin tracking note state for successive calls to add_event
1320 MidiRegionView::begin_write()
1322 assert(!_active_notes);
1323 _active_notes = new CanvasNote*[128];
1324 for (unsigned i=0; i < 128; ++i) {
1325 _active_notes[i] = 0;
1330 /** Destroy note state for add_event
1333 MidiRegionView::end_write()
1335 delete[] _active_notes;
1337 _marked_for_selection.clear();
1338 _marked_for_velocity.clear();
1342 /** Resolve an active MIDI note (while recording).
1345 MidiRegionView::resolve_note(uint8_t note, double end_time)
1347 if (midi_view()->note_mode() != Sustained) {
1351 if (_active_notes && _active_notes[note]) {
1352 const framepos_t end_time_frames = beats_to_frames(end_time) - _region->start();
1353 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1354 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1355 _active_notes[note] = 0;
1360 /** Extend active notes to rightmost edge of region (if length is changed)
1363 MidiRegionView::extend_active_notes()
1365 if (!_active_notes) {
1369 for (unsigned i=0; i < 128; ++i) {
1370 if (_active_notes[i]) {
1371 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1378 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1380 if (no_sound_notes || !trackview.editor().sound_notes()) {
1384 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1386 if (!route_ui || !route_ui->midi_track()) {
1390 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1396 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1398 if (no_sound_notes || !trackview.editor().sound_notes()) {
1402 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1404 if (!route_ui || !route_ui->midi_track()) {
1408 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1410 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1419 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1421 const framepos_t note_start_frames = beats_to_frames(note->time());
1423 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1424 (note_start_frames < _region->start());
1426 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1427 (note->note() <= midi_stream_view()->highest_note());
1433 MidiRegionView::update_note (CanvasNote* ev)
1435 boost::shared_ptr<NoteType> note = ev->note();
1437 const framepos_t note_start_frames = beats_to_frames(note->time());
1439 /* trim note display to not overlap the end of its region */
1440 const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1442 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1443 const double y1 = midi_stream_view()->note_to_y(note->note());
1444 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1446 ev->property_x1() = x;
1447 ev->property_y1() = y1;
1448 if (note->length() > 0) {
1449 ev->property_x2() = note_endpixel;
1451 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1453 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1455 if (note->length() == 0) {
1456 if (_active_notes) {
1457 assert(note->note() < 128);
1458 // If this note is already active there's a stuck note,
1459 // finish the old note rectangle
1460 if (_active_notes[note->note()]) {
1461 CanvasNote* const old_rect = _active_notes[note->note()];
1462 boost::shared_ptr<NoteType> old_note = old_rect->note();
1463 old_rect->property_x2() = x;
1464 old_rect->property_outline_what() = (guint32) 0xF;
1466 _active_notes[note->note()] = ev;
1468 /* outline all but right edge */
1469 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1471 /* outline all edges */
1472 ev->property_outline_what() = (guint32) 0xF;
1477 MidiRegionView::update_hit (CanvasHit* ev)
1479 boost::shared_ptr<NoteType> note = ev->note();
1481 const framepos_t note_start_frames = beats_to_frames(note->time());
1482 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1483 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1484 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1488 return diamond_size;
1491 /** Add a MIDI note to the view (with length).
1493 * If in sustained mode, notes with length 0 will be considered active
1494 * notes, and resolve_note should be called when the corresponding note off
1495 * event arrives, to properly display the note.
1498 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1500 CanvasNoteEvent* event = 0;
1502 assert(note->time() >= 0);
1503 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1505 if (midi_view()->note_mode() == Sustained) {
1507 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1509 update_note (ev_rect);
1513 MidiGhostRegion* gr;
1515 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1516 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1517 gr->add_note(ev_rect);
1521 } else if (midi_view()->note_mode() == Percussive) {
1523 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1525 CanvasHit* ev_diamond = new CanvasHit(*this, *_note_group, diamond_size, note);
1527 update_hit (ev_diamond);
1536 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1537 note_selected(event, true);
1540 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1541 event->show_velocity();
1543 event->on_channel_selection_change(_last_channel_selection);
1544 _events.push_back(event);
1555 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1556 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1558 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1560 /* potentially extend region to hold new note */
1562 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1563 framepos_t region_end = _region->position() + _region->length() - 1;
1565 if (end_frame > region_end) {
1566 _region->set_length (end_frame - _region->position(), this);
1569 _marked_for_selection.clear ();
1572 start_note_diff_command (_("step add"));
1573 note_diff_add_note (new_note, true, false);
1576 // last_step_edit_note = new_note;
1580 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1582 change_note_lengths (false, false, beats, false, true);
1586 MidiRegionView::add_canvas_program_change (PCEvent& program, const string& displaytext)
1588 assert(program.time >= 0);
1590 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1592 double height = midi_stream_view()->contents_height();
1594 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1595 new CanvasProgramChange(*this, *_note_group,
1600 _custom_device_mode,
1601 program.time, program.channel, program.value));
1603 // Show unless program change is beyond the region bounds
1604 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1610 _pgm_changes.push_back(pgm_change);
1614 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1616 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1617 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1619 if (msb_control != 0) {
1620 msb = int(msb_control->get_double(true, time));
1623 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1624 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1626 if (lsb_control != 0) {
1627 lsb = lsb_control->get_double(true, time);
1630 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1631 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1632 double program_number = -1.0;
1633 if (program_control != 0) {
1634 program_number = program_control->get_double(true, time);
1637 key.msb = (int) floor(msb + 0.5);
1638 key.lsb = (int) floor(lsb + 0.5);
1639 key.program_number = (int) floor(program_number + 0.5);
1640 assert(key.is_sane());
1645 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1647 // TODO: Get the real event here and alter them at the original times
1648 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1649 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1650 if (msb_control != 0) {
1651 msb_control->set_double(double(new_patch.msb), true, old_program.time);
1654 // TODO: Get the real event here and alter them at the original times
1655 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1656 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1657 if (lsb_control != 0) {
1658 lsb_control->set_double(double(new_patch.lsb), true, old_program.time);
1661 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1662 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1664 assert(program_control != 0);
1665 program_control->set_double(float(new_patch.program_number), true, old_program.time);
1667 _pgm_changes.clear ();
1668 display_program_changes (); // XXX would be nice to limit to just old_program.channel
1671 /** @param t Time in frames relative to region position */
1673 MidiRegionView::add_program_change (framecnt_t t, uint8_t channel, uint8_t value)
1675 boost::shared_ptr<Evoral::Control> control = midi_region()->model()->control (
1676 Evoral::Parameter (MidiPgmChangeAutomation, channel, 0), true
1681 Evoral::MusicalTime const b = frames_to_beats (t + midi_region()->start());
1683 control->list()->add (b, value);
1685 _pgm_changes.clear ();
1686 display_program_changes ();
1690 MidiRegionView::move_program_change (PCEvent pc, Evoral::MusicalTime t)
1692 boost::shared_ptr<Evoral::Control> control = _model->control (Evoral::Parameter (MidiPgmChangeAutomation, pc.channel, 0));
1695 control->list()->erase (pc.time, pc.value);
1696 control->list()->add (t, pc.value);
1698 _pgm_changes.clear ();
1699 display_program_changes ();
1703 MidiRegionView::delete_program_change (CanvasProgramChange* pc)
1705 boost::shared_ptr<Evoral::Control> control = _model->control (Evoral::Parameter (MidiPgmChangeAutomation, pc->channel(), 0));
1708 control->list()->erase (pc->event_time(), pc->program());
1709 _pgm_changes.clear ();
1710 display_program_changes ();
1714 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1716 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1717 alter_program_change(program_change_event, new_patch);
1721 MidiRegionView::previous_program(CanvasProgramChange& program)
1723 if (program.program() < 127) {
1724 MIDI::Name::PatchPrimaryKey key;
1725 get_patch_key_at(program.event_time(), program.channel(), key);
1726 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1728 key.program_number++;
1729 alter_program_change(program_change_event, key);
1734 MidiRegionView::next_program(CanvasProgramChange& program)
1736 if (program.program() > 0) {
1737 MIDI::Name::PatchPrimaryKey key;
1738 get_patch_key_at(program.event_time(), program.channel(), key);
1739 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1741 key.program_number--;
1742 alter_program_change(program_change_event, key);
1747 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1749 if (_selection.empty()) {
1753 if (_selection.erase (cne) > 0) {
1754 cerr << "Erased a CNE from selection\n";
1759 MidiRegionView::delete_selection()
1761 if (_selection.empty()) {
1765 start_note_diff_command (_("delete selection"));
1767 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1768 if ((*i)->selected()) {
1769 _note_diff_command->remove((*i)->note());
1779 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1781 start_note_diff_command (_("delete note"));
1782 _note_diff_command->remove (n);
1785 trackview.editor().hide_verbose_canvas_cursor ();
1789 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1791 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1792 if ((*i)->selected() && (*i) != ev) {
1793 (*i)->set_selected(false);
1794 (*i)->hide_velocity();
1802 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1804 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1807 Selection::iterator tmp = i;
1810 (*i)->set_selected (false);
1811 _selection.erase (i);
1820 /* don't bother with removing this regionview from the editor selection,
1821 since we're about to add another note, and thus put/keep this
1822 regionview in the editor selection.
1825 if (!ev->selected()) {
1826 add_to_selection (ev);
1831 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1833 uint8_t low_note = 127;
1834 uint8_t high_note = 0;
1835 MidiModel::Notes& notes (_model->notes());
1836 _optimization_iterator = _events.begin();
1842 if (extend && _selection.empty()) {
1848 /* scan existing selection to get note range */
1850 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1851 if ((*i)->note()->note() < low_note) {
1852 low_note = (*i)->note()->note();
1854 if ((*i)->note()->note() > high_note) {
1855 high_note = (*i)->note()->note();
1859 low_note = min (low_note, notenum);
1860 high_note = max (high_note, notenum);
1863 no_sound_notes = true;
1865 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1867 boost::shared_ptr<NoteType> note (*n);
1868 CanvasNoteEvent* cne;
1869 bool select = false;
1871 if (((1 << note->channel()) & channel_mask) != 0) {
1873 if ((note->note() >= low_note && note->note() <= high_note)) {
1876 } else if (note->note() == notenum) {
1882 if ((cne = find_canvas_note (note)) != 0) {
1883 // extend is false because we've taken care of it,
1884 // since it extends by time range, not pitch.
1885 note_selected (cne, add, false);
1889 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1893 no_sound_notes = false;
1897 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1899 MidiModel::Notes& notes (_model->notes());
1900 _optimization_iterator = _events.begin();
1902 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1904 boost::shared_ptr<NoteType> note (*n);
1905 CanvasNoteEvent* cne;
1907 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1908 if ((cne = find_canvas_note (note)) != 0) {
1909 if (cne->selected()) {
1910 note_deselected (cne);
1912 note_selected (cne, true, false);
1920 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1923 clear_selection_except(ev);
1928 if (!ev->selected()) {
1929 add_to_selection (ev);
1933 /* find end of latest note selected, select all between that and the start of "ev" */
1935 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1936 Evoral::MusicalTime latest = 0;
1938 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1939 if ((*i)->note()->end_time() > latest) {
1940 latest = (*i)->note()->end_time();
1942 if ((*i)->note()->time() < earliest) {
1943 earliest = (*i)->note()->time();
1947 if (ev->note()->end_time() > latest) {
1948 latest = ev->note()->end_time();
1951 if (ev->note()->time() < earliest) {
1952 earliest = ev->note()->time();
1955 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1957 /* find notes entirely within OR spanning the earliest..latest range */
1959 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1960 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1961 add_to_selection (*i);
1965 /* if events were guaranteed to be time sorted, we could do this.
1966 but as of sept 10th 2009, they no longer are.
1969 if ((*i)->note()->time() > latest) {
1978 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1980 remove_from_selection (ev);
1984 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1994 // TODO: Make this faster by storing the last updated selection rect, and only
1995 // adjusting things that are in the area that appears/disappeared.
1996 // We probably need a tree to be able to find events in O(log(n)) time.
1998 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2000 /* check if any corner of the note is inside the rect
2003 1) this is computing "touched by", not "contained by" the rect.
2004 2) this does not require that events be sorted in time.
2007 const double ix1 = (*i)->x1();
2008 const double ix2 = (*i)->x2();
2009 const double iy1 = (*i)->y1();
2010 const double iy2 = (*i)->y2();
2012 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2013 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2014 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2015 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2018 if (!(*i)->selected()) {
2019 add_to_selection (*i);
2021 } else if ((*i)->selected()) {
2022 // Not inside rectangle
2023 remove_from_selection (*i);
2029 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2031 Selection::iterator i = _selection.find (ev);
2033 if (i != _selection.end()) {
2034 _selection.erase (i);
2037 ev->set_selected (false);
2038 ev->hide_velocity ();
2040 if (_selection.empty()) {
2041 PublicEditor& editor (trackview.editor());
2042 editor.get_selection().remove (this);
2047 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2049 bool add_mrv_selection = false;
2051 if (_selection.empty()) {
2052 add_mrv_selection = true;
2055 if (_selection.insert (ev).second) {
2056 ev->set_selected (true);
2057 play_midi_note ((ev)->note());
2060 if (add_mrv_selection) {
2061 PublicEditor& editor (trackview.editor());
2062 editor.get_selection().add (this);
2067 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2069 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2070 PossibleChord to_play;
2071 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2073 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2074 if ((*i)->note()->time() < earliest) {
2075 earliest = (*i)->note()->time();
2079 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2080 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2081 to_play.push_back ((*i)->note());
2083 (*i)->move_event(dx, dy);
2086 if (dy && !_selection.empty() && !no_sound_notes && trackview.editor().sound_notes()) {
2088 if (to_play.size() > 1) {
2090 PossibleChord shifted;
2092 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2093 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2094 moved_note->set_note (moved_note->note() + cumulative_dy);
2095 shifted.push_back (moved_note);
2098 play_midi_chord (shifted);
2100 } else if (!to_play.empty()) {
2102 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2103 moved_note->set_note (moved_note->note() + cumulative_dy);
2104 play_midi_note (moved_note);
2110 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2112 assert (!_selection.empty());
2114 uint8_t lowest_note_in_selection = 127;
2115 uint8_t highest_note_in_selection = 0;
2116 uint8_t highest_note_difference = 0;
2118 // find highest and lowest notes first
2120 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2121 uint8_t pitch = (*i)->note()->note();
2122 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2123 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2127 cerr << "dnote: " << (int) dnote << endl;
2128 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2129 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2130 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2131 << int(highest_note_in_selection) << endl;
2132 cerr << "selection size: " << _selection.size() << endl;
2133 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2136 // Make sure the note pitch does not exceed the MIDI standard range
2137 if (highest_note_in_selection + dnote > 127) {
2138 highest_note_difference = highest_note_in_selection - 127;
2141 start_note_diff_command (_("move notes"));
2143 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2145 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2151 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2153 uint8_t original_pitch = (*i)->note()->note();
2154 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2156 // keep notes in standard midi range
2157 clamp_to_0_127(new_pitch);
2159 // keep original pitch if note is dragged outside valid midi range
2160 if ((original_pitch != 0 && new_pitch == 0)
2161 || (original_pitch != 127 && new_pitch == 127)) {
2162 new_pitch = original_pitch;
2165 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2166 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2168 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2173 // care about notes being moved beyond the upper/lower bounds on the canvas
2174 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2175 highest_note_in_selection > midi_stream_view()->highest_note()) {
2176 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2181 MidiRegionView::snap_pixel_to_frame(double x)
2183 PublicEditor& editor = trackview.editor();
2184 // x is region relative, convert it to global absolute frames
2185 framepos_t frame = editor.pixel_to_frame(x) + _region->position();
2186 editor.snap_to(frame);
2187 return frame - _region->position(); // convert back to region relative
2191 MidiRegionView::snap_frame_to_frame(framepos_t x)
2193 PublicEditor& editor = trackview.editor();
2194 // x is region relative, convert it to global absolute frames
2195 framepos_t frame = x + _region->position();
2196 editor.snap_to(frame);
2197 return frame - _region->position(); // convert back to region relative
2201 MidiRegionView::snap_to_pixel(double x)
2203 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2207 MidiRegionView::get_position_pixels()
2209 framepos_t region_frame = get_position();
2210 return trackview.editor().frame_to_pixel(region_frame);
2214 MidiRegionView::get_end_position_pixels()
2216 framepos_t frame = get_position() + get_duration ();
2217 return trackview.editor().frame_to_pixel(frame);
2221 MidiRegionView::beats_to_frames(double beats) const
2223 return _time_converter.to(beats);
2227 MidiRegionView::frames_to_beats(framepos_t frames) const
2229 return _time_converter.from(frames);
2233 MidiRegionView::begin_resizing (bool /*at_front*/)
2235 _resize_data.clear();
2237 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2238 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2240 // only insert CanvasNotes into the map
2242 NoteResizeData *resize_data = new NoteResizeData();
2243 resize_data->canvas_note = note;
2245 // create a new SimpleRect from the note which will be the resize preview
2246 SimpleRect *resize_rect = new SimpleRect(
2247 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2249 // calculate the colors: get the color settings
2250 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2251 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2254 // make the resize preview notes more transparent and bright
2255 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2257 // calculate color based on note velocity
2258 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2259 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2263 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2264 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2266 resize_data->resize_rect = resize_rect;
2267 _resize_data.push_back(resize_data);
2272 /** Update resizing notes while user drags.
2273 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2274 * @param at_front which end of the note (true == note on, false == note off)
2275 * @param delta_x change in mouse position since the start of the drag
2276 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2277 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2278 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2279 * as the \a primary note.
2282 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2284 bool cursor_set = false;
2286 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2287 SimpleRect* resize_rect = (*i)->resize_rect;
2288 CanvasNote* canvas_note = (*i)->canvas_note;
2293 current_x = canvas_note->x1() + delta_x;
2295 current_x = primary->x1() + delta_x;
2299 current_x = canvas_note->x2() + delta_x;
2301 current_x = primary->x2() + delta_x;
2306 resize_rect->property_x1() = snap_to_pixel(current_x);
2307 resize_rect->property_x2() = canvas_note->x2();
2309 resize_rect->property_x2() = snap_to_pixel(current_x);
2310 resize_rect->property_x1() = canvas_note->x1();
2316 beats = snap_pixel_to_frame (current_x);
2317 beats = frames_to_beats (beats);
2322 if (beats < canvas_note->note()->end_time()) {
2323 len = canvas_note->note()->time() - beats;
2324 len += canvas_note->note()->length();
2329 if (beats >= canvas_note->note()->time()) {
2330 len = beats - canvas_note->note()->time();
2337 snprintf (buf, sizeof (buf), "%.3g beats", len);
2338 trackview.editor().show_verbose_canvas_cursor_with (buf);
2347 /** Finish resizing notes when the user releases the mouse button.
2348 * Parameters the same as for \a update_resizing().
2351 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2353 start_note_diff_command (_("resize notes"));
2355 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2356 CanvasNote* canvas_note = (*i)->canvas_note;
2357 SimpleRect* resize_rect = (*i)->resize_rect;
2362 current_x = canvas_note->x1() + delta_x;
2364 current_x = primary->x1() + delta_x;
2368 current_x = canvas_note->x2() + delta_x;
2370 current_x = primary->x2() + delta_x;
2374 current_x = snap_pixel_to_frame (current_x);
2375 current_x = frames_to_beats (current_x);
2377 if (at_front && current_x < canvas_note->note()->end_time()) {
2378 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2380 double len = canvas_note->note()->time() - current_x;
2381 len += canvas_note->note()->length();
2384 /* XXX convert to beats */
2385 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2390 double len = current_x - canvas_note->note()->time();
2393 /* XXX convert to beats */
2394 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2402 _resize_data.clear();
2407 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2409 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, (uint8_t) channel);
2413 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2415 uint8_t new_velocity;
2418 new_velocity = event->note()->velocity() + velocity;
2419 clamp_to_0_127(new_velocity);
2421 new_velocity = velocity;
2424 event->set_selected (event->selected()); // change color
2426 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2430 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2435 new_note = event->note()->note() + note;
2440 clamp_to_0_127 (new_note);
2441 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2445 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2447 bool change_start = false;
2448 bool change_length = false;
2449 Evoral::MusicalTime new_start = 0;
2450 Evoral::MusicalTime new_length = 0;
2452 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2454 front_delta: if positive - move the start of the note later in time (shortening it)
2455 if negative - move the start of the note earlier in time (lengthening it)
2457 end_delta: if positive - move the end of the note later in time (lengthening it)
2458 if negative - move the end of the note earlier in time (shortening it)
2462 if (front_delta < 0) {
2464 if (event->note()->time() < -front_delta) {
2467 new_start = event->note()->time() + front_delta; // moves earlier
2470 /* start moved toward zero, so move the end point out to where it used to be.
2471 Note that front_delta is negative, so this increases the length.
2474 new_length = event->note()->length() - front_delta;
2475 change_start = true;
2476 change_length = true;
2480 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2482 if (new_pos < event->note()->end_time()) {
2483 new_start = event->note()->time() + front_delta;
2484 /* start moved toward the end, so move the end point back to where it used to be */
2485 new_length = event->note()->length() - front_delta;
2486 change_start = true;
2487 change_length = true;
2494 bool can_change = true;
2495 if (end_delta < 0) {
2496 if (event->note()->length() < -end_delta) {
2502 new_length = event->note()->length() + end_delta;
2503 change_length = true;
2508 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2511 if (change_length) {
2512 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2517 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2519 Evoral::MusicalTime new_time;
2523 if (event->note()->time() < -delta) {
2526 new_time = event->note()->time() + delta;
2529 new_time = event->note()->time() + delta;
2535 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2539 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2541 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2545 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2549 if (_selection.empty()) {
2564 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2565 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2571 start_note_diff_command (_("change velocities"));
2573 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2574 Selection::iterator next = i;
2576 change_note_velocity (*i, delta, true);
2582 if (!_selection.empty()) {
2584 snprintf (buf, sizeof (buf), "Vel %d",
2585 (int) (*_selection.begin())->note()->velocity());
2586 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 10);
2592 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2594 if (_selection.empty()) {
2611 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2613 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2617 if ((int8_t) (*i)->note()->note() + delta > 127) {
2624 start_note_diff_command (_("transpose"));
2626 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2627 Selection::iterator next = i;
2629 change_note_note (*i, delta, true);
2637 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2643 /* grab the current grid distance */
2645 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2647 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2648 cerr << "Grid type not available as beats - TO BE FIXED\n";
2658 start_note_diff_command (_("change note lengths"));
2660 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2661 Selection::iterator next = i;
2664 /* note the negation of the delta for start */
2666 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2675 MidiRegionView::nudge_notes (bool forward)
2677 if (_selection.empty()) {
2681 /* pick a note as the point along the timeline to get the nudge distance.
2682 its not necessarily the earliest note, so we may want to pull the notes out
2683 into a vector and sort before using the first one.
2686 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2688 framepos_t distance;
2690 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2692 /* grid is off - use nudge distance */
2694 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2700 framepos_t next_pos = ref_point;
2703 if (max_framepos - 1 < next_pos) {
2707 if (next_pos == 0) {
2713 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2714 distance = ref_point - next_pos;
2717 if (distance == 0) {
2721 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2727 start_note_diff_command (_("nudge"));
2729 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2730 Selection::iterator next = i;
2732 change_note_time (*i, delta, true);
2740 MidiRegionView::change_channel(uint8_t channel)
2742 start_note_diff_command(_("change channel"));
2743 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2744 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2752 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2754 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2756 pre_enter_cursor = editor->get_canvas_cursor ();
2758 if (_mouse_state == SelectTouchDragging) {
2759 note_selected (ev, true);
2762 show_verbose_canvas_cursor (ev->note ());
2766 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2768 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2770 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2771 (*i)->hide_velocity ();
2774 editor->hide_verbose_canvas_cursor ();
2776 if (pre_enter_cursor) {
2777 editor->set_canvas_cursor (pre_enter_cursor);
2778 pre_enter_cursor = 0;
2783 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
2785 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2787 if (x_fraction > 0.0 && x_fraction < 0.25) {
2788 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
2789 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
2790 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
2792 if (pre_enter_cursor && can_set_cursor) {
2793 editor->set_canvas_cursor (pre_enter_cursor);
2799 MidiRegionView::set_frame_color()
2802 if (_selected && should_show_selection) {
2803 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2805 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2811 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2815 case FilterChannels:
2816 _force_channel = -1;
2819 _force_channel = mask;
2820 mask = 0xFFFF; // Show all notes as active (below)
2823 // Update notes for selection
2824 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2825 (*i)->on_channel_selection_change(mask);
2828 _last_channel_selection = mask;
2832 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2834 _model_name = model;
2835 _custom_device_mode = custom_device_mode;
2840 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2842 if (_selection.empty()) {
2846 PublicEditor& editor (trackview.editor());
2851 editor.get_cut_buffer().add (selection_as_cut_buffer());
2859 start_note_diff_command();
2861 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2867 note_diff_remove_note (*i);
2877 MidiRegionView::selection_as_cut_buffer () const
2881 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2882 NoteType* n = (*i)->note().get();
2883 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2886 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2893 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
2899 start_note_diff_command (_("paste"));
2901 Evoral::MusicalTime beat_delta;
2902 Evoral::MusicalTime paste_pos_beats;
2903 Evoral::MusicalTime duration;
2904 Evoral::MusicalTime end_point = 0;
2906 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2907 paste_pos_beats = frames_to_beats (pos - _region->position());
2908 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2909 paste_pos_beats = 0;
2913 for (int n = 0; n < (int) times; ++n) {
2915 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2917 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2918 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2920 /* make all newly added notes selected */
2922 note_diff_add_note (copied_note, true);
2923 end_point = copied_note->end_time();
2926 paste_pos_beats += duration;
2929 /* if we pasted past the current end of the region, extend the region */
2931 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
2932 framepos_t region_end = _region->position() + _region->length() - 1;
2934 if (end_frame > region_end) {
2936 trackview.session()->begin_reversible_command (_("paste"));
2938 _region->clear_changes ();
2939 _region->set_length (end_frame, this);
2940 trackview.session()->add_command (new StatefulDiffCommand (_region));
2946 struct EventNoteTimeEarlyFirstComparator {
2947 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2948 return a->note()->time() < b->note()->time();
2953 MidiRegionView::time_sort_events ()
2955 if (!_sort_needed) {
2959 EventNoteTimeEarlyFirstComparator cmp;
2962 _sort_needed = false;
2966 MidiRegionView::goto_next_note ()
2968 // framepos_t pos = -1;
2969 bool use_next = false;
2971 if (_events.back()->selected()) {
2975 time_sort_events ();
2977 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2978 if ((*i)->selected()) {
2981 } else if (use_next) {
2983 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2988 /* use the first one */
2990 unique_select (_events.front());
2995 MidiRegionView::goto_previous_note ()
2997 // framepos_t pos = -1;
2998 bool use_next = false;
3000 if (_events.front()->selected()) {
3004 time_sort_events ();
3006 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3007 if ((*i)->selected()) {
3010 } else if (use_next) {
3012 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3017 /* use the last one */
3019 unique_select (*(_events.rbegin()));
3023 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3025 bool had_selected = false;
3027 time_sort_events ();
3029 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3030 if ((*i)->selected()) {
3031 selected.insert ((*i)->note());
3032 had_selected = true;
3036 if (allow_all_if_none_selected && !had_selected) {
3037 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3038 selected.insert ((*i)->note());
3044 MidiRegionView::update_ghost_note (double x, double y)
3049 _note_group->w2i (x, y);
3050 framepos_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
3051 trackview.editor().snap_to (f);
3052 f -= _region->position ();
3055 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
3060 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
3062 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
3063 _ghost_note->note()->set_length (length);
3064 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3066 update_note (_ghost_note);
3068 show_verbose_canvas_cursor (_ghost_note->note ());
3072 MidiRegionView::create_ghost_note (double x, double y)
3077 boost::shared_ptr<NoteType> g (new NoteType);
3078 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3079 update_ghost_note (x, y);
3080 _ghost_note->show ();
3085 show_verbose_canvas_cursor (_ghost_note->note ());
3089 MidiRegionView::snap_changed ()
3095 create_ghost_note (_last_ghost_x, _last_ghost_y);
3099 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
3102 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
3103 Evoral::midi_note_name (n->note()).c_str(),
3105 (int) n->velocity());
3106 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 20);
3110 MidiRegionView::drop_down_keys ()
3112 _mouse_state = None;
3116 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3118 double note = midi_stream_view()->y_to_note(y);
3120 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3122 cerr << "Selecting by position\n";
3124 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3126 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3127 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3128 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3129 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3134 bool add_mrv_selection = false;
3136 if (_selection.empty()) {
3137 add_mrv_selection = true;
3140 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3141 if (_selection.insert (*i).second) {
3142 (*i)->set_selected (true);
3146 if (add_mrv_selection) {
3147 PublicEditor& editor (trackview.editor());
3148 editor.get_selection().add (this);
3153 MidiRegionView::color_handler ()
3155 RegionView::color_handler ();
3157 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3158 (*i)->set_selected ((*i)->selected()); // will change color
3161 /* XXX probably more to do here */
3165 MidiRegionView::enable_display (bool yn)
3167 RegionView::enable_display (yn);
3174 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3176 if (_step_edit_cursor == 0) {
3177 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3179 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3180 _step_edit_cursor->property_y1() = 0;
3181 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3182 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3183 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3186 move_step_edit_cursor (pos);
3187 _step_edit_cursor->show ();
3191 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3193 _step_edit_cursor_position = pos;
3195 if (_step_edit_cursor) {
3196 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3197 _step_edit_cursor->property_x1() = pixel;
3198 set_step_edit_cursor_width (_step_edit_cursor_width);
3203 MidiRegionView::hide_step_edit_cursor ()
3205 if (_step_edit_cursor) {
3206 _step_edit_cursor->hide ();
3211 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3213 _step_edit_cursor_width = beats;
3215 if (_step_edit_cursor) {
3216 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
3220 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3221 * @param buf Data that has been recorded.
3222 * @param w Source that this data will end up in.
3225 MidiRegionView::data_recorded (boost::shared_ptr<MidiBuffer> buf, boost::weak_ptr<MidiSource> w)
3227 if (!_active_notes) {
3228 /* we aren't actively being recorded to */
3232 boost::shared_ptr<MidiSource> src = w.lock ();
3233 if (!src || src != midi_region()->midi_source()) {
3234 /* recorded data was not destined for our source */
3238 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3239 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3241 framepos_t back = max_framepos;
3243 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3244 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3245 assert (ev.buffer ());
3247 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3249 if (ev.type() == MIDI_CMD_NOTE_ON) {
3251 boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > note (
3252 new Evoral::Note<Evoral::MusicalTime> (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3255 add_note (note, true);
3257 /* fix up our note range */
3258 if (ev.note() < _current_range_min) {
3259 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3260 } else if (ev.note() > _current_range_max) {
3261 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3264 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3265 resolve_note (ev.note (), time_beats);
3271 midi_stream_view()->check_record_layers (region(), back);
3275 MidiRegionView::trim_front_starting ()
3277 /* Reparent the note group to the region view's parent, so that it doesn't change
3278 when the region view is trimmed.
3280 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3281 _temporary_note_group->move (group->property_x(), group->property_y());
3282 _note_group->reparent (*_temporary_note_group);
3286 MidiRegionView::trim_front_ending ()
3288 _note_group->reparent (*group);
3289 delete _temporary_note_group;
3290 _temporary_note_group = 0;
3292 if (_region->start() < 0) {
3293 /* Trim drag made start time -ve; fix this */
3294 midi_region()->fix_negative_start ();