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)
103 , pre_enter_cursor (0)
105 _note_group->raise_to_top();
106 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
108 connect_to_diskstream ();
111 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
112 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
113 TimeAxisViewItem::Visibility visibility)
114 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
116 , _last_channel_selection(0xFFFF)
117 , _model_name(string())
118 , _custom_device_mode(string())
120 , _note_group(new ArdourCanvas::Group(*parent))
124 , _step_edit_cursor (0)
125 , _step_edit_cursor_width (1.0)
126 , _step_edit_cursor_position (0.0)
129 , _sort_needed (true)
130 , _optimization_iterator (_events.end())
132 , no_sound_notes (false)
134 _note_group->raise_to_top();
135 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
137 connect_to_diskstream ();
140 MidiRegionView::MidiRegionView (const MidiRegionView& other)
141 : sigc::trackable(other)
144 , _last_channel_selection(0xFFFF)
145 , _model_name(string())
146 , _custom_device_mode(string())
148 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
152 , _step_edit_cursor (0)
153 , _step_edit_cursor_width (1.0)
154 , _step_edit_cursor_position (0.0)
157 , _sort_needed (true)
158 , _optimization_iterator (_events.end())
160 , no_sound_notes (false)
165 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
166 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
171 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
172 : RegionView (other, boost::shared_ptr<Region> (region))
174 , _last_channel_selection(0xFFFF)
175 , _model_name(string())
176 , _custom_device_mode(string())
178 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
182 , _step_edit_cursor (0)
183 , _step_edit_cursor_width (1.0)
184 , _step_edit_cursor_position (0.0)
187 , _sort_needed (true)
188 , _optimization_iterator (_events.end())
190 , no_sound_notes (false)
195 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
196 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
202 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
204 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
206 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
207 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
211 midi_region()->midi_source(0)->load_model();
214 _model = midi_region()->midi_source(0)->model();
215 _enable_display = false;
217 RegionView::init (basic_color, false);
219 compute_colors (basic_color);
221 set_height (trackview.current_height());
224 region_sync_changed ();
225 region_resized (ARDOUR::bounds_change);
228 reset_width_dependent_items (_pixel_width);
232 _enable_display = true;
235 display_model (_model);
239 group->raise_to_top();
240 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
242 midi_view()->signal_channel_mode_changed().connect(
243 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
245 midi_view()->signal_midi_patch_settings_changed().connect(
246 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
248 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
250 connect_to_diskstream ();
254 MidiRegionView::connect_to_diskstream ()
256 midi_view()->midi_track()->DataRecorded.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::data_recorded, this, _1, _2), gui_context ());
260 MidiRegionView::canvas_event(GdkEvent* ev)
262 if (!trackview.editor().internal_editing()) {
266 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
267 to its items, which means that ev->type == GDK_SCROLL will never be seen
272 return scroll (&ev->scroll);
275 return key_press (&ev->key);
277 case GDK_KEY_RELEASE:
278 return key_release (&ev->key);
280 case GDK_BUTTON_PRESS:
281 return button_press (&ev->button);
283 case GDK_2BUTTON_PRESS:
286 case GDK_BUTTON_RELEASE:
287 return button_release (&ev->button);
289 case GDK_ENTER_NOTIFY:
290 return enter_notify (&ev->crossing);
292 case GDK_LEAVE_NOTIFY:
293 return leave_notify (&ev->crossing);
295 case GDK_MOTION_NOTIFY:
296 return motion (&ev->motion);
306 MidiRegionView::enter_notify (GdkEventCrossing* ev)
308 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
310 Keyboard::magic_widget_grab_focus();
313 if (trackview.editor().current_mouse_mode() == MouseRange) {
314 create_ghost_note (ev->x, ev->y);
321 MidiRegionView::leave_notify (GdkEventCrossing*)
323 trackview.editor().hide_verbose_canvas_cursor ();
330 MidiRegionView::button_press (GdkEventButton* ev)
334 group->w2i (_last_x, _last_y);
336 if (_mouse_state != SelectTouchDragging && ev->button == 1) {
337 _pressed_button = ev->button;
338 _mouse_state = Pressed;
342 _pressed_button = ev->button;
348 MidiRegionView::button_release (GdkEventButton* ev)
350 double event_x, event_y;
351 framepos_t event_frame = 0;
355 group->w2i(event_x, event_y);
356 group->ungrab(ev->time);
357 event_frame = trackview.editor().pixel_to_frame(event_x);
359 if (ev->button == 3) {
361 } else if (_pressed_button != 1) {
365 switch (_mouse_state) {
366 case Pressed: // Clicked
367 switch (trackview.editor().current_mouse_mode()) {
371 maybe_select_by_position (ev, event_x, event_y);
377 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
381 create_note_at (event_x, event_y, beats, true);
389 case SelectRectDragging: // Select drag done
395 case AddDragging: // Add drag done
397 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
398 const double x = _drag_rect->property_x1();
399 const double length = trackview.editor().pixel_to_frame
400 (_drag_rect->property_x2() - _drag_rect->property_x1());
402 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), false);
408 create_ghost_note (ev->x, ev->y);
418 MidiRegionView::motion (GdkEventMotion* ev)
420 double event_x, event_y;
421 framepos_t event_frame = 0;
425 group->w2i(event_x, event_y);
427 // convert event_x to global frame
428 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
429 trackview.editor().snap_to(event_frame);
430 // convert event_frame back to local coordinates relative to position
431 event_frame -= _region->position();
434 update_ghost_note (ev->x, ev->y);
437 /* any motion immediately hides velocity text that may have been visible */
439 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
440 (*i)->hide_velocity ();
443 switch (_mouse_state) {
444 case Pressed: // Maybe start a drag, if we've moved a bit
446 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
447 /* no appreciable movement since the button was pressed */
452 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
453 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
454 Gdk::Cursor(Gdk::FLEUR), ev->time);
457 _drag_start_x = event_x;
458 _drag_start_y = event_y;
460 _drag_rect = new ArdourCanvas::SimpleRect(*group);
461 _drag_rect->property_x1() = event_x;
462 _drag_rect->property_y1() = event_y;
463 _drag_rect->property_x2() = event_x;
464 _drag_rect->property_y2() = event_y;
465 _drag_rect->property_outline_what() = 0xFF;
466 _drag_rect->property_outline_color_rgba()
467 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
468 _drag_rect->property_fill_color_rgba()
469 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
471 _mouse_state = SelectRectDragging;
474 // Add note drag start
475 } else if (trackview.editor().internal_editing()) {
480 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
481 Gdk::Cursor(Gdk::FLEUR), ev->time);
484 _drag_start_x = event_x;
485 _drag_start_y = event_y;
487 _drag_rect = new ArdourCanvas::SimpleRect(*group);
488 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
490 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
491 midi_stream_view()->y_to_note(event_y));
492 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
493 _drag_rect->property_y2() = _drag_rect->property_y1()
494 + floor(midi_stream_view()->note_height());
495 _drag_rect->property_outline_what() = 0xFF;
496 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
497 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
499 _mouse_state = AddDragging;
505 case SelectRectDragging: // Select drag motion
506 case AddDragging: // Add note drag motion
510 GdkModifierType state;
511 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
516 if (_mouse_state == AddDragging)
517 event_x = trackview.editor().frame_to_pixel(event_frame);
520 if (event_x > _drag_start_x)
521 _drag_rect->property_x2() = event_x;
523 _drag_rect->property_x1() = event_x;
526 if (_drag_rect && _mouse_state == SelectRectDragging) {
527 if (event_y > _drag_start_y)
528 _drag_rect->property_y2() = event_y;
530 _drag_rect->property_y1() = event_y;
532 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
538 case SelectTouchDragging:
550 MidiRegionView::scroll (GdkEventScroll* ev)
552 if (_selection.empty()) {
556 trackview.editor().hide_verbose_canvas_cursor ();
558 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
560 if (ev->direction == GDK_SCROLL_UP) {
561 change_velocities (true, fine, false);
562 } else if (ev->direction == GDK_SCROLL_DOWN) {
563 change_velocities (false, fine, false);
569 MidiRegionView::key_press (GdkEventKey* ev)
571 /* since GTK bindings are generally activated on press, and since
572 detectable auto-repeat is the name of the game and only sends
573 repeated presses, carry out key actions at key press, not release.
576 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
577 _mouse_state = SelectTouchDragging;
580 } else if (ev->keyval == GDK_Escape) {
584 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
586 bool start = (ev->keyval == GDK_comma);
587 bool end = (ev->keyval == GDK_period);
588 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
589 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
591 change_note_lengths (fine, shorter, 0.0, start, end);
595 } else if (ev->keyval == GDK_Delete) {
600 } else if (ev->keyval == GDK_Tab) {
602 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
603 goto_previous_note ();
609 } else if (ev->keyval == GDK_Up) {
611 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
612 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
614 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
615 change_velocities (true, fine, allow_smush);
617 transpose (true, fine, allow_smush);
621 } else if (ev->keyval == GDK_Down) {
623 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
624 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
626 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
627 change_velocities (false, fine, allow_smush);
629 transpose (false, fine, allow_smush);
633 } else if (ev->keyval == GDK_Left) {
638 } else if (ev->keyval == GDK_Right) {
643 } else if (ev->keyval == GDK_Control_L) {
652 MidiRegionView::key_release (GdkEventKey* ev)
654 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
662 MidiRegionView::show_list_editor ()
665 _list_editor = new MidiListEditor (trackview.session(), midi_region());
667 _list_editor->present ();
670 /** Add a note to the model, and the view, at a canvas (click) coordinate.
671 * \param x horizontal position in pixels
672 * \param y vertical position in pixels
673 * \param length duration of the note in beats, which will be snapped to the grid
674 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
677 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
679 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
680 MidiStreamView* const view = mtv->midi_view();
682 double note = midi_stream_view()->y_to_note(y);
685 assert(note <= 127.0);
687 // Start of note in frames relative to region start
688 framepos_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
689 assert(start_frames >= 0);
692 length = frames_to_beats(
693 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
695 assert (length != 0);
698 length = frames_to_beats (beats_to_frames (length) - 1);
701 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
705 /* pick the highest selected channel, unless all channels are selected,
706 which is interpreted to mean channel 1 (zero)
709 for (uint16_t i = 0; i < 16; ++i) {
710 if (chn_mask & (1<<i)) {
720 const boost::shared_ptr<NoteType> new_note (new NoteType (channel,
721 frames_to_beats(start_frames + _region->start()), length,
722 (uint8_t)note, 0x40));
724 if (_model->contains (new_note)) {
728 view->update_note_range(new_note->note());
730 MidiModel::DiffCommand* cmd = _model->new_diff_command("add note");
732 _model->apply_command(*trackview.session(), cmd);
734 play_midi_note (new_note);
738 MidiRegionView::clear_events()
743 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
744 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
749 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
754 _pgm_changes.clear();
756 _optimization_iterator = _events.end();
760 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
764 content_connection.disconnect ();
765 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
769 if (_enable_display) {
775 MidiRegionView::start_diff_command(string name)
777 if (!_diff_command) {
778 _diff_command = _model->new_diff_command(name);
783 MidiRegionView::diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
786 _diff_command->add(note);
789 _marked_for_selection.insert(note);
792 _marked_for_velocity.insert(note);
797 MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
799 if (_diff_command && ev->note()) {
800 _diff_command->remove(ev->note());
805 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
806 MidiModel::DiffCommand::Property property,
810 _diff_command->change (ev->note(), property, val);
815 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
816 MidiModel::DiffCommand::Property property,
817 Evoral::MusicalTime val)
820 _diff_command->change (ev->note(), property, val);
825 MidiRegionView::apply_diff ()
829 if (!_diff_command) {
833 if ((add_or_remove = _diff_command->adds_or_removes())) {
834 // Mark all selected notes for selection when model reloads
835 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
836 _marked_for_selection.insert((*i)->note());
840 _model->apply_command(*trackview.session(), _diff_command);
842 midi_view()->midi_track()->playlist_modified();
845 _marked_for_selection.clear();
848 _marked_for_velocity.clear();
852 MidiRegionView::apply_diff_as_subcommand()
856 if (!_diff_command) {
860 if ((add_or_remove = _diff_command->adds_or_removes())) {
861 // Mark all selected notes for selection when model reloads
862 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
863 _marked_for_selection.insert((*i)->note());
867 _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
869 midi_view()->midi_track()->playlist_modified();
872 _marked_for_selection.clear();
874 _marked_for_velocity.clear();
879 MidiRegionView::abort_command()
881 delete _diff_command;
887 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
889 if (_optimization_iterator != _events.end()) {
890 ++_optimization_iterator;
893 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
894 return *_optimization_iterator;
897 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
898 if ((*_optimization_iterator)->note() == note) {
899 return *_optimization_iterator;
907 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
909 MidiModel::Notes notes;
910 _model->get_notes (notes, op, val, chan_mask);
912 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
913 CanvasNoteEvent* cne = find_canvas_note (*n);
921 MidiRegionView::redisplay_model()
923 // Don't redisplay the model if we're currently recording and displaying that
929 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
933 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
937 MidiModel::ReadLock lock(_model->read_lock());
939 MidiModel::Notes& notes (_model->notes());
940 _optimization_iterator = _events.begin();
942 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
944 boost::shared_ptr<NoteType> note (*n);
945 CanvasNoteEvent* cne;
948 if (note_in_region_range (note, visible)) {
950 if ((cne = find_canvas_note (note)) != 0) {
957 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
959 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
971 add_note (note, visible);
976 if ((cne = find_canvas_note (note)) != 0) {
984 /* remove note items that are no longer valid */
986 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
987 if (!(*i)->valid ()) {
989 i = _events.erase (i);
995 _pgm_changes.clear();
999 display_program_changes();
1001 _marked_for_selection.clear ();
1002 _marked_for_velocity.clear ();
1004 /* we may have caused _events to contain things out of order (e.g. if a note
1005 moved earlier or later). we don't generally need them in time order, but
1006 make a note that a sort is required for those cases that require it.
1009 _sort_needed = true;
1013 MidiRegionView::display_program_changes()
1015 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1016 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1018 for (uint8_t i = 0; i < 16; ++i) {
1019 if (chn_mask & (1<<i)) {
1020 display_program_changes_on_channel (i);
1026 MidiRegionView::display_program_changes_on_channel(uint8_t channel)
1028 boost::shared_ptr<Evoral::Control> control =
1029 _model->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation, channel));
1035 Glib::Mutex::Lock lock (control->list()->lock());
1037 for (AutomationList::const_iterator event = control->list()->begin();
1038 event != control->list()->end(); ++event) {
1039 double event_time = (*event)->when;
1040 double program_number = floor((*event)->value + 0.5);
1042 // Get current value of bank select MSB at time of the program change
1043 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1044 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1046 if (msb_control != 0) {
1047 msb = uint8_t(floor(msb_control->get_double(true, event_time) + 0.5));
1050 // Get current value of bank select LSB at time of the program change
1051 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1052 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1054 if (lsb_control != 0) {
1055 lsb = uint8_t(floor(lsb_control->get_double(true, event_time) + 0.5));
1058 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
1060 boost::shared_ptr<MIDI::Name::Patch> patch =
1061 MIDI::Name::MidiPatchManager::instance().find_patch(
1062 _model_name, _custom_device_mode, channel, patch_key);
1064 PCEvent program_change(event_time, uint8_t(program_number), channel);
1067 add_pgm_change(program_change, patch->name());
1070 // program_number is zero-based: convert to one-based
1071 snprintf(buf, 4, "%d", int(program_number+1));
1072 add_pgm_change(program_change, buf);
1078 MidiRegionView::display_sysexes()
1080 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1081 Evoral::MusicalTime time = (*i)->time();
1086 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1087 str << int((*i)->buffer()[b]);
1088 if (b != (*i)->size() -1) {
1092 string text = str.str();
1094 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1096 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1098 double height = midi_stream_view()->contents_height();
1100 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1101 new CanvasSysEx(*this, *group, text, height, x, 1.0));
1103 // Show unless program change is beyond the region bounds
1104 if (time - _region->start() >= _region->length() || time < _region->start()) {
1110 _sys_exes.push_back(sysex);
1115 MidiRegionView::~MidiRegionView ()
1117 in_destructor = true;
1119 trackview.editor().hide_verbose_canvas_cursor ();
1121 note_delete_connection.disconnect ();
1123 delete _list_editor;
1125 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1127 if (_active_notes) {
1135 delete _diff_command;
1136 delete _step_edit_cursor;
1140 MidiRegionView::region_resized (const PropertyChange& what_changed)
1142 RegionView::region_resized(what_changed);
1144 if (what_changed.contains (ARDOUR::Properties::position)) {
1145 set_duration(_region->length(), 0);
1146 if (_enable_display) {
1153 MidiRegionView::reset_width_dependent_items (double pixel_width)
1155 RegionView::reset_width_dependent_items(pixel_width);
1156 assert(_pixel_width == pixel_width);
1158 if (_enable_display) {
1162 move_step_edit_cursor (_step_edit_cursor_position);
1163 set_step_edit_cursor_width (_step_edit_cursor_width);
1167 MidiRegionView::set_height (double height)
1169 static const double FUDGE = 2.0;
1170 const double old_height = _height;
1171 RegionView::set_height(height);
1172 _height = height - FUDGE;
1174 apply_note_range(midi_stream_view()->lowest_note(),
1175 midi_stream_view()->highest_note(),
1176 height != old_height + FUDGE);
1179 name_pixbuf->raise_to_top();
1182 for (PgmChanges::iterator x = _pgm_changes.begin(); x != _pgm_changes.end(); ++x) {
1183 (*x)->set_height (midi_stream_view()->contents_height());
1186 if (_step_edit_cursor) {
1187 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1192 /** Apply the current note range from the stream view
1193 * by repositioning/hiding notes as necessary
1196 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1198 if (!_enable_display) {
1202 if (!force && _current_range_min == min && _current_range_max == max) {
1206 _current_range_min = min;
1207 _current_range_max = max;
1209 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1210 CanvasNoteEvent* event = *i;
1211 boost::shared_ptr<NoteType> note (event->note());
1213 if (note->note() < _current_range_min ||
1214 note->note() > _current_range_max) {
1220 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1222 const double y1 = midi_stream_view()->note_to_y(note->note());
1223 const double y2 = y1 + floor(midi_stream_view()->note_height());
1225 cnote->property_y1() = y1;
1226 cnote->property_y2() = y2;
1228 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1230 const double diamond_size = update_hit (chit);
1232 chit->set_height (diamond_size);
1238 MidiRegionView::add_ghost (TimeAxisView& tv)
1242 double unit_position = _region->position () / samples_per_unit;
1243 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1244 MidiGhostRegion* ghost;
1246 if (mtv && mtv->midi_view()) {
1247 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1248 to allow having midi notes on top of note lines and waveforms.
1250 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1252 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1255 ghost->set_height ();
1256 ghost->set_duration (_region->length() / samples_per_unit);
1257 ghosts.push_back (ghost);
1259 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1260 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1261 ghost->add_note(note);
1265 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1271 /** Begin tracking note state for successive calls to add_event
1274 MidiRegionView::begin_write()
1276 assert(!_active_notes);
1277 _active_notes = new CanvasNote*[128];
1278 for (unsigned i=0; i < 128; ++i) {
1279 _active_notes[i] = 0;
1284 /** Destroy note state for add_event
1287 MidiRegionView::end_write()
1289 delete[] _active_notes;
1291 _marked_for_selection.clear();
1292 _marked_for_velocity.clear();
1296 /** Resolve an active MIDI note (while recording).
1299 MidiRegionView::resolve_note(uint8_t note, double end_time)
1301 if (midi_view()->note_mode() != Sustained) {
1305 if (_active_notes && _active_notes[note]) {
1306 const framepos_t end_time_frames = beats_to_frames(end_time) - _region->start();
1307 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1308 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1309 _active_notes[note] = 0;
1314 /** Extend active notes to rightmost edge of region (if length is changed)
1317 MidiRegionView::extend_active_notes()
1319 if (!_active_notes) {
1323 for (unsigned i=0; i < 128; ++i) {
1324 if (_active_notes[i]) {
1325 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1332 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1334 if (no_sound_notes || !trackview.editor().sound_notes()) {
1338 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1340 if (!route_ui || !route_ui->midi_track()) {
1344 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1350 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1352 if (no_sound_notes || !trackview.editor().sound_notes()) {
1356 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1358 if (!route_ui || !route_ui->midi_track()) {
1362 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1364 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1373 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1375 const framepos_t note_start_frames = beats_to_frames(note->time());
1377 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1378 (note_start_frames < _region->start());
1380 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1381 (note->note() <= midi_stream_view()->highest_note());
1387 MidiRegionView::update_note (CanvasNote* ev)
1389 boost::shared_ptr<NoteType> note = ev->note();
1391 const framepos_t note_start_frames = beats_to_frames(note->time());
1393 /* trim note display to not overlap the end of its region */
1394 const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1396 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1397 const double y1 = midi_stream_view()->note_to_y(note->note());
1398 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1400 ev->property_x1() = x;
1401 ev->property_y1() = y1;
1402 if (note->length() > 0) {
1403 ev->property_x2() = note_endpixel;
1405 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1407 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1409 if (note->length() == 0) {
1410 if (_active_notes) {
1411 assert(note->note() < 128);
1412 // If this note is already active there's a stuck note,
1413 // finish the old note rectangle
1414 if (_active_notes[note->note()]) {
1415 CanvasNote* const old_rect = _active_notes[note->note()];
1416 boost::shared_ptr<NoteType> old_note = old_rect->note();
1417 old_rect->property_x2() = x;
1418 old_rect->property_outline_what() = (guint32) 0xF;
1420 _active_notes[note->note()] = ev;
1422 /* outline all but right edge */
1423 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1425 /* outline all edges */
1426 ev->property_outline_what() = (guint32) 0xF;
1431 MidiRegionView::update_hit (CanvasHit* ev)
1433 boost::shared_ptr<NoteType> note = ev->note();
1435 const framepos_t note_start_frames = beats_to_frames(note->time());
1436 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1437 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1438 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1442 return diamond_size;
1445 /** Add a MIDI note to the view (with length).
1447 * If in sustained mode, notes with length 0 will be considered active
1448 * notes, and resolve_note should be called when the corresponding note off
1449 * event arrives, to properly display the note.
1452 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1454 CanvasNoteEvent* event = 0;
1456 assert(note->time() >= 0);
1457 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1459 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1461 if (midi_view()->note_mode() == Sustained) {
1463 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1465 update_note (ev_rect);
1469 MidiGhostRegion* gr;
1471 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1472 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1473 gr->add_note(ev_rect);
1477 } else if (midi_view()->note_mode() == Percussive) {
1479 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1481 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1483 update_hit (ev_diamond);
1492 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1493 note_selected(event, true);
1496 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1497 event->show_velocity();
1499 event->on_channel_selection_change(_last_channel_selection);
1500 _events.push_back(event);
1511 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1512 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1514 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1516 /* potentially extend region to hold new note */
1518 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1519 framepos_t region_end = _region->position() + _region->length() - 1;
1521 if (end_frame > region_end) {
1522 _region->set_length (end_frame - _region->position(), this);
1525 _marked_for_selection.clear ();
1528 start_diff_command (_("step add"));
1529 diff_add_note (new_note, true, false);
1532 // last_step_edit_note = new_note;
1536 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1538 change_note_lengths (false, false, beats, false, true);
1542 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1544 assert(program.time >= 0);
1546 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1547 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1549 double height = midi_stream_view()->contents_height();
1551 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1552 new CanvasProgramChange(*this, *group,
1557 _custom_device_mode,
1558 program.time, program.channel, program.value));
1560 // Show unless program change is beyond the region bounds
1561 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1567 _pgm_changes.push_back(pgm_change);
1571 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1573 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1574 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1576 if (msb_control != 0) {
1577 msb = int(msb_control->get_double(true, time));
1580 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1581 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1583 if (lsb_control != 0) {
1584 lsb = lsb_control->get_double(true, time);
1587 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1588 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1589 double program_number = -1.0;
1590 if (program_control != 0) {
1591 program_number = program_control->get_double(true, time);
1594 key.msb = (int) floor(msb + 0.5);
1595 key.lsb = (int) floor(lsb + 0.5);
1596 key.program_number = (int) floor(program_number + 0.5);
1597 assert(key.is_sane());
1602 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1604 // TODO: Get the real event here and alter them at the original times
1605 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1606 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1607 if (msb_control != 0) {
1608 msb_control->set_double(double(new_patch.msb), true, old_program.time);
1611 // TODO: Get the real event here and alter them at the original times
1612 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1613 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1614 if (lsb_control != 0) {
1615 lsb_control->set_double(double(new_patch.lsb), true, old_program.time);
1618 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1619 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1621 assert(program_control != 0);
1622 program_control->set_double(float(new_patch.program_number), true, old_program.time);
1624 _pgm_changes.clear ();
1625 display_program_changes (); // XXX would be nice to limit to just old_program.channel
1629 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1631 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1632 alter_program_change(program_change_event, new_patch);
1636 MidiRegionView::previous_program(CanvasProgramChange& program)
1638 if (program.program() < 127) {
1639 MIDI::Name::PatchPrimaryKey key;
1640 get_patch_key_at(program.event_time(), program.channel(), key);
1641 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1643 key.program_number++;
1644 alter_program_change(program_change_event, key);
1649 MidiRegionView::next_program(CanvasProgramChange& program)
1651 if (program.program() > 0) {
1652 MIDI::Name::PatchPrimaryKey key;
1653 get_patch_key_at(program.event_time(), program.channel(), key);
1654 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1656 key.program_number--;
1657 alter_program_change(program_change_event, key);
1662 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1664 if (_selection.empty()) {
1668 if (_selection.erase (cne) > 0) {
1669 cerr << "Erased a CNE from selection\n";
1674 MidiRegionView::delete_selection()
1676 if (_selection.empty()) {
1680 start_diff_command (_("delete selection"));
1682 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1683 if ((*i)->selected()) {
1684 _diff_command->remove((*i)->note());
1694 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1696 start_diff_command (_("delete note"));
1697 _diff_command->remove (n);
1700 trackview.editor().hide_verbose_canvas_cursor ();
1704 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1706 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1707 if ((*i)->selected() && (*i) != ev) {
1708 (*i)->set_selected(false);
1709 (*i)->hide_velocity();
1717 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1719 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1722 Selection::iterator tmp = i;
1725 (*i)->set_selected (false);
1726 _selection.erase (i);
1735 /* don't bother with removing this regionview from the editor selection,
1736 since we're about to add another note, and thus put/keep this
1737 regionview in the editor selection.
1740 if (!ev->selected()) {
1741 add_to_selection (ev);
1746 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1748 uint8_t low_note = 127;
1749 uint8_t high_note = 0;
1750 MidiModel::Notes& notes (_model->notes());
1751 _optimization_iterator = _events.begin();
1757 if (extend && _selection.empty()) {
1763 /* scan existing selection to get note range */
1765 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1766 if ((*i)->note()->note() < low_note) {
1767 low_note = (*i)->note()->note();
1769 if ((*i)->note()->note() > high_note) {
1770 high_note = (*i)->note()->note();
1774 low_note = min (low_note, notenum);
1775 high_note = max (high_note, notenum);
1778 no_sound_notes = true;
1780 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1782 boost::shared_ptr<NoteType> note (*n);
1783 CanvasNoteEvent* cne;
1784 bool select = false;
1786 if (((1 << note->channel()) & channel_mask) != 0) {
1788 if ((note->note() >= low_note && note->note() <= high_note)) {
1791 } else if (note->note() == notenum) {
1797 if ((cne = find_canvas_note (note)) != 0) {
1798 // extend is false because we've taken care of it,
1799 // since it extends by time range, not pitch.
1800 note_selected (cne, add, false);
1804 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1808 no_sound_notes = false;
1812 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1814 MidiModel::Notes& notes (_model->notes());
1815 _optimization_iterator = _events.begin();
1817 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1819 boost::shared_ptr<NoteType> note (*n);
1820 CanvasNoteEvent* cne;
1822 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1823 if ((cne = find_canvas_note (note)) != 0) {
1824 if (cne->selected()) {
1825 note_deselected (cne);
1827 note_selected (cne, true, false);
1835 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1838 clear_selection_except(ev);
1843 if (!ev->selected()) {
1844 add_to_selection (ev);
1848 /* find end of latest note selected, select all between that and the start of "ev" */
1850 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1851 Evoral::MusicalTime latest = 0;
1853 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1854 if ((*i)->note()->end_time() > latest) {
1855 latest = (*i)->note()->end_time();
1857 if ((*i)->note()->time() < earliest) {
1858 earliest = (*i)->note()->time();
1862 if (ev->note()->end_time() > latest) {
1863 latest = ev->note()->end_time();
1866 if (ev->note()->time() < earliest) {
1867 earliest = ev->note()->time();
1870 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1872 /* find notes entirely within OR spanning the earliest..latest range */
1874 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1875 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1876 add_to_selection (*i);
1880 /* if events were guaranteed to be time sorted, we could do this.
1881 but as of sept 10th 2009, they no longer are.
1884 if ((*i)->note()->time() > latest) {
1893 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1895 remove_from_selection (ev);
1899 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1909 // TODO: Make this faster by storing the last updated selection rect, and only
1910 // adjusting things that are in the area that appears/disappeared.
1911 // We probably need a tree to be able to find events in O(log(n)) time.
1913 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1915 /* check if any corner of the note is inside the rect
1918 1) this is computing "touched by", not "contained by" the rect.
1919 2) this does not require that events be sorted in time.
1922 const double ix1 = (*i)->x1();
1923 const double ix2 = (*i)->x2();
1924 const double iy1 = (*i)->y1();
1925 const double iy2 = (*i)->y2();
1927 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1928 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1929 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1930 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1933 if (!(*i)->selected()) {
1934 add_to_selection (*i);
1936 } else if ((*i)->selected()) {
1937 // Not inside rectangle
1938 remove_from_selection (*i);
1944 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1946 Selection::iterator i = _selection.find (ev);
1948 if (i != _selection.end()) {
1949 _selection.erase (i);
1952 ev->set_selected (false);
1953 ev->hide_velocity ();
1955 if (_selection.empty()) {
1956 PublicEditor& editor (trackview.editor());
1957 editor.get_selection().remove (this);
1962 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
1964 bool add_mrv_selection = false;
1966 if (_selection.empty()) {
1967 add_mrv_selection = true;
1970 if (_selection.insert (ev).second) {
1971 ev->set_selected (true);
1972 play_midi_note ((ev)->note());
1975 if (add_mrv_selection) {
1976 PublicEditor& editor (trackview.editor());
1977 editor.get_selection().add (this);
1982 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
1984 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
1985 PossibleChord to_play;
1986 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1988 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1989 if ((*i)->note()->time() < earliest) {
1990 earliest = (*i)->note()->time();
1994 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1995 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
1996 to_play.push_back ((*i)->note());
1998 (*i)->move_event(dx, dy);
2001 if (dy && !_selection.empty() && !no_sound_notes && trackview.editor().sound_notes()) {
2003 if (to_play.size() > 1) {
2005 PossibleChord shifted;
2007 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2008 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2009 moved_note->set_note (moved_note->note() + cumulative_dy);
2010 shifted.push_back (moved_note);
2013 play_midi_chord (shifted);
2015 } else if (!to_play.empty()) {
2017 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2018 moved_note->set_note (moved_note->note() + cumulative_dy);
2019 play_midi_note (moved_note);
2025 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2027 assert (!_selection.empty());
2029 uint8_t lowest_note_in_selection = 127;
2030 uint8_t highest_note_in_selection = 0;
2031 uint8_t highest_note_difference = 0;
2033 // find highest and lowest notes first
2035 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2036 uint8_t pitch = (*i)->note()->note();
2037 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2038 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2042 cerr << "dnote: " << (int) dnote << endl;
2043 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2044 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2045 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2046 << int(highest_note_in_selection) << endl;
2047 cerr << "selection size: " << _selection.size() << endl;
2048 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2051 // Make sure the note pitch does not exceed the MIDI standard range
2052 if (highest_note_in_selection + dnote > 127) {
2053 highest_note_difference = highest_note_in_selection - 127;
2056 start_diff_command(_("move notes"));
2058 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2060 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2066 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
2068 uint8_t original_pitch = (*i)->note()->note();
2069 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2071 // keep notes in standard midi range
2072 clamp_to_0_127(new_pitch);
2074 // keep original pitch if note is dragged outside valid midi range
2075 if ((original_pitch != 0 && new_pitch == 0)
2076 || (original_pitch != 127 && new_pitch == 127)) {
2077 new_pitch = original_pitch;
2080 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2081 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2083 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
2088 // care about notes being moved beyond the upper/lower bounds on the canvas
2089 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2090 highest_note_in_selection > midi_stream_view()->highest_note()) {
2091 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2096 MidiRegionView::snap_pixel_to_frame(double x)
2098 PublicEditor& editor = trackview.editor();
2099 // x is region relative, convert it to global absolute frames
2100 framepos_t frame = editor.pixel_to_frame(x) + _region->position();
2101 editor.snap_to(frame);
2102 return frame - _region->position(); // convert back to region relative
2106 MidiRegionView::snap_frame_to_frame(framepos_t x)
2108 PublicEditor& editor = trackview.editor();
2109 // x is region relative, convert it to global absolute frames
2110 framepos_t frame = x + _region->position();
2111 editor.snap_to(frame);
2112 return frame - _region->position(); // convert back to region relative
2116 MidiRegionView::snap_to_pixel(double x)
2118 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2122 MidiRegionView::get_position_pixels()
2124 framepos_t region_frame = get_position();
2125 return trackview.editor().frame_to_pixel(region_frame);
2129 MidiRegionView::get_end_position_pixels()
2131 framepos_t frame = get_position() + get_duration ();
2132 return trackview.editor().frame_to_pixel(frame);
2136 MidiRegionView::beats_to_frames(double beats) const
2138 return _time_converter.to(beats);
2142 MidiRegionView::frames_to_beats(framepos_t frames) const
2144 return _time_converter.from(frames);
2148 MidiRegionView::begin_resizing (bool /*at_front*/)
2150 _resize_data.clear();
2152 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2153 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2155 // only insert CanvasNotes into the map
2157 NoteResizeData *resize_data = new NoteResizeData();
2158 resize_data->canvas_note = note;
2160 // create a new SimpleRect from the note which will be the resize preview
2161 SimpleRect *resize_rect = new SimpleRect(
2162 *group, note->x1(), note->y1(), note->x2(), note->y2());
2164 // calculate the colors: get the color settings
2165 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2166 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2169 // make the resize preview notes more transparent and bright
2170 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2172 // calculate color based on note velocity
2173 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2174 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2178 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2179 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2181 resize_data->resize_rect = resize_rect;
2182 _resize_data.push_back(resize_data);
2187 /** Update resizing notes while user drags.
2188 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2189 * @param at_front which end of the note (true == note on, false == note off)
2190 * @param delta_x change in mouse position since the start of the drag
2191 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2192 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2193 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2194 * as the \a primary note.
2197 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2199 bool cursor_set = false;
2201 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2202 SimpleRect* resize_rect = (*i)->resize_rect;
2203 CanvasNote* canvas_note = (*i)->canvas_note;
2208 current_x = canvas_note->x1() + delta_x;
2210 current_x = primary->x1() + delta_x;
2214 current_x = canvas_note->x2() + delta_x;
2216 current_x = primary->x2() + delta_x;
2221 resize_rect->property_x1() = snap_to_pixel(current_x);
2222 resize_rect->property_x2() = canvas_note->x2();
2224 resize_rect->property_x2() = snap_to_pixel(current_x);
2225 resize_rect->property_x1() = canvas_note->x1();
2231 beats = snap_pixel_to_frame (current_x);
2232 beats = frames_to_beats (beats);
2237 if (beats < canvas_note->note()->end_time()) {
2238 len = canvas_note->note()->time() - beats;
2239 len += canvas_note->note()->length();
2244 if (beats >= canvas_note->note()->time()) {
2245 len = beats - canvas_note->note()->time();
2252 snprintf (buf, sizeof (buf), "%.3g beats", len);
2253 trackview.editor().show_verbose_canvas_cursor_with (buf);
2262 /** Finish resizing notes when the user releases the mouse button.
2263 * Parameters the same as for \a update_resizing().
2266 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2268 start_diff_command(_("resize notes"));
2270 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2271 CanvasNote* canvas_note = (*i)->canvas_note;
2272 SimpleRect* resize_rect = (*i)->resize_rect;
2277 current_x = canvas_note->x1() + delta_x;
2279 current_x = primary->x1() + delta_x;
2283 current_x = canvas_note->x2() + delta_x;
2285 current_x = primary->x2() + delta_x;
2289 current_x = snap_pixel_to_frame (current_x);
2290 current_x = frames_to_beats (current_x);
2292 if (at_front && current_x < canvas_note->note()->end_time()) {
2293 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2295 double len = canvas_note->note()->time() - current_x;
2296 len += canvas_note->note()->length();
2299 /* XXX convert to beats */
2300 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2305 double len = current_x - canvas_note->note()->time();
2308 /* XXX convert to beats */
2309 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2317 _resize_data.clear();
2322 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2324 diff_add_change (event, MidiModel::DiffCommand::Channel, (uint8_t) channel);
2328 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2330 uint8_t new_velocity;
2333 new_velocity = event->note()->velocity() + velocity;
2334 clamp_to_0_127(new_velocity);
2336 new_velocity = velocity;
2339 event->set_selected (event->selected()); // change color
2341 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2345 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2350 new_note = event->note()->note() + note;
2355 clamp_to_0_127 (new_note);
2356 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2360 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2362 bool change_start = false;
2363 bool change_length = false;
2364 Evoral::MusicalTime new_start = 0;
2365 Evoral::MusicalTime new_length = 0;
2367 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2369 front_delta: if positive - move the start of the note later in time (shortening it)
2370 if negative - move the start of the note earlier in time (lengthening it)
2372 end_delta: if positive - move the end of the note later in time (lengthening it)
2373 if negative - move the end of the note earlier in time (shortening it)
2377 if (front_delta < 0) {
2379 if (event->note()->time() < -front_delta) {
2382 new_start = event->note()->time() + front_delta; // moves earlier
2385 /* start moved toward zero, so move the end point out to where it used to be.
2386 Note that front_delta is negative, so this increases the length.
2389 new_length = event->note()->length() - front_delta;
2390 change_start = true;
2391 change_length = true;
2395 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2397 if (new_pos < event->note()->end_time()) {
2398 new_start = event->note()->time() + front_delta;
2399 /* start moved toward the end, so move the end point back to where it used to be */
2400 new_length = event->note()->length() - front_delta;
2401 change_start = true;
2402 change_length = true;
2409 bool can_change = true;
2410 if (end_delta < 0) {
2411 if (event->note()->length() < -end_delta) {
2417 new_length = event->note()->length() + end_delta;
2418 change_length = true;
2423 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2426 if (change_length) {
2427 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2432 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2434 Evoral::MusicalTime new_time;
2438 if (event->note()->time() < -delta) {
2441 new_time = event->note()->time() + delta;
2444 new_time = event->note()->time() + delta;
2450 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2454 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2456 diff_add_change (event, MidiModel::DiffCommand::Length, t);
2460 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2464 if (_selection.empty()) {
2479 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2480 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2486 start_diff_command(_("change velocities"));
2488 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2489 Selection::iterator next = i;
2491 change_note_velocity (*i, delta, true);
2497 if (!_selection.empty()) {
2499 snprintf (buf, sizeof (buf), "Vel %d",
2500 (int) (*_selection.begin())->note()->velocity());
2501 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 10);
2507 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2509 if (_selection.empty()) {
2526 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2528 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2532 if ((int8_t) (*i)->note()->note() + delta > 127) {
2539 start_diff_command (_("transpose"));
2541 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2542 Selection::iterator next = i;
2544 change_note_note (*i, delta, true);
2552 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2558 /* grab the current grid distance */
2560 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2562 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2563 cerr << "Grid type not available as beats - TO BE FIXED\n";
2573 start_diff_command (_("change note lengths"));
2575 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2576 Selection::iterator next = i;
2579 /* note the negation of the delta for start */
2581 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2590 MidiRegionView::nudge_notes (bool forward)
2592 if (_selection.empty()) {
2596 /* pick a note as the point along the timeline to get the nudge distance.
2597 its not necessarily the earliest note, so we may want to pull the notes out
2598 into a vector and sort before using the first one.
2601 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2603 framepos_t distance;
2605 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2607 /* grid is off - use nudge distance */
2609 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2615 framepos_t next_pos = ref_point;
2618 if (max_framepos - 1 < next_pos) {
2622 if (next_pos == 0) {
2628 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2629 distance = ref_point - next_pos;
2632 if (distance == 0) {
2636 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2642 start_diff_command (_("nudge"));
2644 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2645 Selection::iterator next = i;
2647 change_note_time (*i, delta, true);
2655 MidiRegionView::change_channel(uint8_t channel)
2657 start_diff_command(_("change channel"));
2658 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2659 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2667 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2669 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2671 pre_enter_cursor = editor->get_canvas_cursor ();
2673 if (_mouse_state == SelectTouchDragging) {
2674 note_selected (ev, true);
2677 show_verbose_canvas_cursor (ev->note ());
2681 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2683 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2685 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2686 (*i)->hide_velocity ();
2689 editor->hide_verbose_canvas_cursor ();
2691 if (pre_enter_cursor) {
2692 editor->set_canvas_cursor (pre_enter_cursor);
2693 pre_enter_cursor = 0;
2698 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
2700 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2702 if (x_fraction > 0.0 && x_fraction < 0.25) {
2703 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
2704 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
2705 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
2707 if (pre_enter_cursor && can_set_cursor) {
2708 editor->set_canvas_cursor (pre_enter_cursor);
2714 MidiRegionView::set_frame_color()
2717 if (_selected && should_show_selection) {
2718 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2720 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2726 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2730 case FilterChannels:
2731 _force_channel = -1;
2734 _force_channel = mask;
2735 mask = 0xFFFF; // Show all notes as active (below)
2738 // Update notes for selection
2739 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2740 (*i)->on_channel_selection_change(mask);
2743 _last_channel_selection = mask;
2747 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2749 _model_name = model;
2750 _custom_device_mode = custom_device_mode;
2755 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2757 if (_selection.empty()) {
2761 PublicEditor& editor (trackview.editor());
2766 editor.get_cut_buffer().add (selection_as_cut_buffer());
2774 start_diff_command();
2776 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2782 diff_remove_note (*i);
2792 MidiRegionView::selection_as_cut_buffer () const
2796 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2797 NoteType* n = (*i)->note().get();
2798 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2801 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2808 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
2814 start_diff_command (_("paste"));
2816 Evoral::MusicalTime beat_delta;
2817 Evoral::MusicalTime paste_pos_beats;
2818 Evoral::MusicalTime duration;
2819 Evoral::MusicalTime end_point = 0;
2821 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2822 paste_pos_beats = frames_to_beats (pos - _region->position());
2823 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2824 paste_pos_beats = 0;
2828 for (int n = 0; n < (int) times; ++n) {
2830 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2832 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2833 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2835 /* make all newly added notes selected */
2837 diff_add_note (copied_note, true);
2838 end_point = copied_note->end_time();
2841 paste_pos_beats += duration;
2844 /* if we pasted past the current end of the region, extend the region */
2846 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
2847 framepos_t region_end = _region->position() + _region->length() - 1;
2849 if (end_frame > region_end) {
2851 trackview.session()->begin_reversible_command (_("paste"));
2853 _region->clear_changes ();
2854 _region->set_length (end_frame, this);
2855 trackview.session()->add_command (new StatefulDiffCommand (_region));
2861 struct EventNoteTimeEarlyFirstComparator {
2862 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2863 return a->note()->time() < b->note()->time();
2868 MidiRegionView::time_sort_events ()
2870 if (!_sort_needed) {
2874 EventNoteTimeEarlyFirstComparator cmp;
2877 _sort_needed = false;
2881 MidiRegionView::goto_next_note ()
2883 // framepos_t pos = -1;
2884 bool use_next = false;
2886 if (_events.back()->selected()) {
2890 time_sort_events ();
2892 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2893 if ((*i)->selected()) {
2896 } else if (use_next) {
2898 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2903 /* use the first one */
2905 unique_select (_events.front());
2910 MidiRegionView::goto_previous_note ()
2912 // framepos_t pos = -1;
2913 bool use_next = false;
2915 if (_events.front()->selected()) {
2919 time_sort_events ();
2921 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2922 if ((*i)->selected()) {
2925 } else if (use_next) {
2927 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2932 /* use the last one */
2934 unique_select (*(_events.rbegin()));
2938 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2940 bool had_selected = false;
2942 time_sort_events ();
2944 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2945 if ((*i)->selected()) {
2946 selected.insert ((*i)->note());
2947 had_selected = true;
2951 if (allow_all_if_none_selected && !had_selected) {
2952 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2953 selected.insert ((*i)->note());
2959 MidiRegionView::update_ghost_note (double x, double y)
2965 framepos_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
2966 trackview.editor().snap_to (f);
2967 f -= _region->position ();
2970 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
2975 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
2977 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
2978 _ghost_note->note()->set_length (length);
2979 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
2981 update_note (_ghost_note);
2983 show_verbose_canvas_cursor (_ghost_note->note ());
2987 MidiRegionView::create_ghost_note (double x, double y)
2992 boost::shared_ptr<NoteType> g (new NoteType);
2993 _ghost_note = new NoEventCanvasNote (*this, *group, g);
2994 update_ghost_note (x, y);
2995 _ghost_note->show ();
3000 show_verbose_canvas_cursor (_ghost_note->note ());
3004 MidiRegionView::snap_changed ()
3010 create_ghost_note (_last_ghost_x, _last_ghost_y);
3014 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
3017 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
3018 Evoral::midi_note_name (n->note()).c_str(),
3020 (int) n->velocity());
3021 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 20);
3025 MidiRegionView::drop_down_keys ()
3027 _mouse_state = None;
3031 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3033 double note = midi_stream_view()->y_to_note(y);
3035 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3037 cerr << "Selecting by position\n";
3039 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3041 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3042 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3043 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3044 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3049 bool add_mrv_selection = false;
3051 if (_selection.empty()) {
3052 add_mrv_selection = true;
3055 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3056 if (_selection.insert (*i).second) {
3057 (*i)->set_selected (true);
3061 if (add_mrv_selection) {
3062 PublicEditor& editor (trackview.editor());
3063 editor.get_selection().add (this);
3068 MidiRegionView::color_handler ()
3070 RegionView::color_handler ();
3072 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3073 (*i)->set_selected ((*i)->selected()); // will change color
3076 /* XXX probably more to do here */
3080 MidiRegionView::enable_display (bool yn)
3082 RegionView::enable_display (yn);
3089 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3091 if (_step_edit_cursor == 0) {
3092 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3094 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3095 _step_edit_cursor->property_y1() = 0;
3096 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3097 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3098 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3101 move_step_edit_cursor (pos);
3102 _step_edit_cursor->show ();
3106 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3108 _step_edit_cursor_position = pos;
3110 if (_step_edit_cursor) {
3111 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3112 _step_edit_cursor->property_x1() = pixel;
3113 set_step_edit_cursor_width (_step_edit_cursor_width);
3118 MidiRegionView::hide_step_edit_cursor ()
3120 if (_step_edit_cursor) {
3121 _step_edit_cursor->hide ();
3126 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3128 _step_edit_cursor_width = beats;
3130 if (_step_edit_cursor) {
3131 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
3135 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3136 * @param buf Data that has been recorded.
3137 * @param w Source that this data will end up in.
3140 MidiRegionView::data_recorded (boost::shared_ptr<MidiBuffer> buf, boost::weak_ptr<MidiSource> w)
3142 if (!_active_notes) {
3143 /* we aren't actively being recorded to */
3147 boost::shared_ptr<MidiSource> src = w.lock ();
3148 if (!src || src != midi_region()->midi_source()) {
3149 /* recorded data was not destined for our source */
3153 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3154 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3156 framepos_t back = max_framepos;
3158 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3159 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3160 assert (ev.buffer ());
3162 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3164 if (ev.type() == MIDI_CMD_NOTE_ON) {
3166 boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > note (
3167 new Evoral::Note<Evoral::MusicalTime> (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3170 add_note (note, true);
3172 /* fix up our note range */
3173 if (ev.note() < _current_range_min) {
3174 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3175 } else if (ev.note() > _current_range_max) {
3176 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3179 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3180 resolve_note (ev.note (), time_beats);
3186 midi_stream_view()->check_record_layers (region(), back);