2 Copyright (C) 2001-2007 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <gtkmm2ext/gtk_ui.h>
29 #include <sigc++/signal.h>
31 #include "pbd/memento_command.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "ardour/playlist.h"
35 #include "ardour/tempo.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_model.h"
39 #include "ardour/midi_patch_manager.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIParameters.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "automation_region_view.h"
48 #include "automation_time_axis.h"
49 #include "canvas-hit.h"
50 #include "canvas-note.h"
51 #include "canvas-program-change.h"
53 #include "ghostregion.h"
54 #include "gui_thread.h"
56 #include "midi_cut_buffer.h"
57 #include "midi_list_editor.h"
58 #include "midi_region_view.h"
59 #include "midi_streamview.h"
60 #include "midi_time_axis.h"
61 #include "midi_time_axis.h"
62 #include "midi_util.h"
63 #include "note_player.h"
64 #include "public_editor.h"
65 #include "rgb_macros.h"
66 #include "selection.h"
67 #include "simpleline.h"
68 #include "streamview.h"
73 using namespace ARDOUR;
75 using namespace Editing;
76 using namespace ArdourCanvas;
77 using Gtkmm2ext::Keyboard;
79 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
80 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
81 : RegionView (parent, tv, r, spu, basic_color)
83 , _last_channel_selection(0xFFFF)
84 , _current_range_min(0)
85 , _current_range_max(0)
86 , _model_name(string())
87 , _custom_device_mode(string())
89 , _note_group(new ArdourCanvas::Group(*parent))
93 , _step_edit_cursor (0)
94 , _step_edit_cursor_width (1.0)
95 , _step_edit_cursor_position (0.0)
99 , _optimization_iterator (_events.end())
101 , no_sound_notes (false)
102 , pre_enter_cursor (0)
104 _note_group->raise_to_top();
105 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
107 connect_to_diskstream ();
110 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
111 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
112 TimeAxisViewItem::Visibility visibility)
113 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
115 , _last_channel_selection(0xFFFF)
116 , _model_name(string())
117 , _custom_device_mode(string())
119 , _note_group(new ArdourCanvas::Group(*parent))
123 , _step_edit_cursor (0)
124 , _step_edit_cursor_width (1.0)
125 , _step_edit_cursor_position (0.0)
128 , _sort_needed (true)
129 , _optimization_iterator (_events.end())
131 , no_sound_notes (false)
133 _note_group->raise_to_top();
134 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
136 connect_to_diskstream ();
139 MidiRegionView::MidiRegionView (const MidiRegionView& other)
140 : sigc::trackable(other)
143 , _last_channel_selection(0xFFFF)
144 , _model_name(string())
145 , _custom_device_mode(string())
147 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
151 , _step_edit_cursor (0)
152 , _step_edit_cursor_width (1.0)
153 , _step_edit_cursor_position (0.0)
156 , _sort_needed (true)
157 , _optimization_iterator (_events.end())
159 , no_sound_notes (false)
164 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
165 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
170 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
171 : RegionView (other, boost::shared_ptr<Region> (region))
173 , _last_channel_selection(0xFFFF)
174 , _model_name(string())
175 , _custom_device_mode(string())
177 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
181 , _step_edit_cursor (0)
182 , _step_edit_cursor_width (1.0)
183 , _step_edit_cursor_position (0.0)
186 , _sort_needed (true)
187 , _optimization_iterator (_events.end())
189 , no_sound_notes (false)
194 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
195 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
201 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
203 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
205 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
206 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
210 midi_region()->midi_source(0)->load_model();
213 _model = midi_region()->midi_source(0)->model();
214 _enable_display = false;
216 RegionView::init (basic_color, false);
218 compute_colors (basic_color);
220 set_height (trackview.current_height());
223 region_sync_changed ();
224 region_resized (ARDOUR::bounds_change);
227 reset_width_dependent_items (_pixel_width);
231 _enable_display = true;
234 display_model (_model);
238 group->raise_to_top();
239 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
241 midi_view()->signal_channel_mode_changed().connect(
242 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
244 midi_view()->signal_midi_patch_settings_changed().connect(
245 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
247 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
249 connect_to_diskstream ();
253 MidiRegionView::connect_to_diskstream ()
255 midi_view()->midi_track()->DataRecorded.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::data_recorded, this, _1, _2), gui_context ());
259 MidiRegionView::canvas_event(GdkEvent* ev)
261 if (!trackview.editor().internal_editing()) {
265 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
266 to its items, which means that ev->type == GDK_SCROLL will never be seen
271 return scroll (&ev->scroll);
274 return key_press (&ev->key);
276 case GDK_KEY_RELEASE:
277 return key_release (&ev->key);
279 case GDK_BUTTON_PRESS:
280 return button_press (&ev->button);
282 case GDK_2BUTTON_PRESS:
285 case GDK_BUTTON_RELEASE:
286 return button_release (&ev->button);
288 case GDK_ENTER_NOTIFY:
289 return enter_notify (&ev->crossing);
291 case GDK_LEAVE_NOTIFY:
292 return leave_notify (&ev->crossing);
294 case GDK_MOTION_NOTIFY:
295 return motion (&ev->motion);
305 MidiRegionView::enter_notify (GdkEventCrossing* ev)
307 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
309 Keyboard::magic_widget_grab_focus();
312 if (trackview.editor().current_mouse_mode() == MouseRange) {
313 create_ghost_note (ev->x, ev->y);
320 MidiRegionView::leave_notify (GdkEventCrossing* ev)
322 trackview.editor().hide_verbose_canvas_cursor ();
329 MidiRegionView::button_press (GdkEventButton* ev)
333 group->w2i (_last_x, _last_y);
335 if (_mouse_state != SelectTouchDragging && ev->button == 1) {
336 _pressed_button = ev->button;
337 _mouse_state = Pressed;
341 _pressed_button = ev->button;
347 MidiRegionView::button_release (GdkEventButton* ev)
349 double event_x, event_y;
350 framepos_t event_frame = 0;
354 group->w2i(event_x, event_y);
355 group->ungrab(ev->time);
356 event_frame = trackview.editor().pixel_to_frame(event_x);
358 if (ev->button == 3) {
360 } else if (_pressed_button != 1) {
364 switch (_mouse_state) {
365 case Pressed: // Clicked
366 switch (trackview.editor().current_mouse_mode()) {
370 maybe_select_by_position (ev, event_x, event_y);
376 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
380 create_note_at (event_x, event_y, beats, true);
388 case SelectRectDragging: // Select drag done
394 case AddDragging: // Add drag done
396 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
397 const double x = _drag_rect->property_x1();
398 const double length = trackview.editor().pixel_to_frame
399 (_drag_rect->property_x2() - _drag_rect->property_x1());
401 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), false);
407 create_ghost_note (ev->x, ev->y);
417 MidiRegionView::motion (GdkEventMotion* ev)
419 double event_x, event_y;
420 framepos_t event_frame = 0;
424 group->w2i(event_x, event_y);
426 // convert event_x to global frame
427 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
428 trackview.editor().snap_to(event_frame);
429 // convert event_frame back to local coordinates relative to position
430 event_frame -= _region->position();
433 update_ghost_note (ev->x, ev->y);
436 /* any motion immediately hides velocity text that may have been visible */
438 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
439 (*i)->hide_velocity ();
442 switch (_mouse_state) {
443 case Pressed: // Maybe start a drag, if we've moved a bit
445 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
446 /* no appreciable movement since the button was pressed */
451 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
452 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
453 Gdk::Cursor(Gdk::FLEUR), ev->time);
456 _drag_start_x = event_x;
457 _drag_start_y = event_y;
459 _drag_rect = new ArdourCanvas::SimpleRect(*group);
460 _drag_rect->property_x1() = event_x;
461 _drag_rect->property_y1() = event_y;
462 _drag_rect->property_x2() = event_x;
463 _drag_rect->property_y2() = event_y;
464 _drag_rect->property_outline_what() = 0xFF;
465 _drag_rect->property_outline_color_rgba()
466 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
467 _drag_rect->property_fill_color_rgba()
468 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
470 _mouse_state = SelectRectDragging;
473 // Add note drag start
474 } else if (trackview.editor().internal_editing()) {
479 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
480 Gdk::Cursor(Gdk::FLEUR), ev->time);
483 _drag_start_x = event_x;
484 _drag_start_y = event_y;
486 _drag_rect = new ArdourCanvas::SimpleRect(*group);
487 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
489 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
490 midi_stream_view()->y_to_note(event_y));
491 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
492 _drag_rect->property_y2() = _drag_rect->property_y1()
493 + floor(midi_stream_view()->note_height());
494 _drag_rect->property_outline_what() = 0xFF;
495 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
496 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
498 _mouse_state = AddDragging;
504 case SelectRectDragging: // Select drag motion
505 case AddDragging: // Add note drag motion
509 GdkModifierType state;
510 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
515 if (_mouse_state == AddDragging)
516 event_x = trackview.editor().frame_to_pixel(event_frame);
519 if (event_x > _drag_start_x)
520 _drag_rect->property_x2() = event_x;
522 _drag_rect->property_x1() = event_x;
525 if (_drag_rect && _mouse_state == SelectRectDragging) {
526 if (event_y > _drag_start_y)
527 _drag_rect->property_y2() = event_y;
529 _drag_rect->property_y1() = event_y;
531 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
537 case SelectTouchDragging:
549 MidiRegionView::scroll (GdkEventScroll* ev)
551 if (_selection.empty()) {
555 trackview.editor().hide_verbose_canvas_cursor ();
557 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
559 if (ev->direction == GDK_SCROLL_UP) {
560 change_velocities (true, fine, false);
561 } else if (ev->direction == GDK_SCROLL_DOWN) {
562 change_velocities (false, fine, false);
568 MidiRegionView::key_press (GdkEventKey* ev)
570 /* since GTK bindings are generally activated on press, and since
571 detectable auto-repeat is the name of the game and only sends
572 repeated presses, carry out key actions at key press, not release.
575 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
576 _mouse_state = SelectTouchDragging;
579 } else if (ev->keyval == GDK_Escape) {
583 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
585 bool start = (ev->keyval == GDK_comma);
586 bool end = (ev->keyval == GDK_period);
587 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
588 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
590 change_note_lengths (fine, shorter, 0.0, start, end);
594 } else if (ev->keyval == GDK_Delete) {
599 } else if (ev->keyval == GDK_Tab) {
601 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
602 goto_previous_note ();
608 } else if (ev->keyval == GDK_Up) {
610 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
611 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
613 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
614 change_velocities (true, fine, allow_smush);
616 transpose (true, fine, allow_smush);
620 } else if (ev->keyval == GDK_Down) {
622 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
623 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
625 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
626 change_velocities (false, fine, allow_smush);
628 transpose (false, fine, allow_smush);
632 } else if (ev->keyval == GDK_Left) {
637 } else if (ev->keyval == GDK_Right) {
642 } else if (ev->keyval == GDK_Control_L) {
651 MidiRegionView::key_release (GdkEventKey* ev)
653 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
661 MidiRegionView::show_list_editor ()
664 _list_editor = new MidiListEditor (trackview.session(), midi_region());
666 _list_editor->present ();
669 /** Add a note to the model, and the view, at a canvas (click) coordinate.
670 * \param x horizontal position in pixels
671 * \param y vertical position in pixels
672 * \param length duration of the note in beats, which will be snapped to the grid
673 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
676 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
678 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
679 MidiStreamView* const view = mtv->midi_view();
681 double note = midi_stream_view()->y_to_note(y);
684 assert(note <= 127.0);
686 // Start of note in frames relative to region start
687 framepos_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
688 assert(start_frames >= 0);
691 length = frames_to_beats(
692 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
694 assert (length != 0);
697 length = frames_to_beats (beats_to_frames (length) - 1);
700 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
704 /* pick the highest selected channel, unless all channels are selected,
705 which is interpreted to mean channel 1 (zero)
708 for (uint16_t i = 0; i < 16; ++i) {
709 if (chn_mask & (1<<i)) {
719 const boost::shared_ptr<NoteType> new_note (new NoteType (channel,
720 frames_to_beats(start_frames + _region->start()), length,
721 (uint8_t)note, 0x40));
723 if (_model->contains (new_note)) {
727 view->update_note_range(new_note->note());
729 MidiModel::DiffCommand* cmd = _model->new_diff_command("add note");
731 _model->apply_command(*trackview.session(), cmd);
733 play_midi_note (new_note);
737 MidiRegionView::clear_events()
742 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
743 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
748 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
753 _pgm_changes.clear();
755 _optimization_iterator = _events.end();
759 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
763 content_connection.disconnect ();
764 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
768 if (_enable_display) {
774 MidiRegionView::start_diff_command(string name)
776 if (!_diff_command) {
777 _diff_command = _model->new_diff_command(name);
782 MidiRegionView::diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
785 _diff_command->add(note);
788 _marked_for_selection.insert(note);
791 _marked_for_velocity.insert(note);
796 MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
798 if (_diff_command && ev->note()) {
799 _diff_command->remove(ev->note());
804 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
805 MidiModel::DiffCommand::Property property,
809 _diff_command->change (ev->note(), property, val);
814 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
815 MidiModel::DiffCommand::Property property,
816 Evoral::MusicalTime val)
819 _diff_command->change (ev->note(), property, val);
824 MidiRegionView::apply_diff ()
828 if (!_diff_command) {
832 if ((add_or_remove = _diff_command->adds_or_removes())) {
833 // Mark all selected notes for selection when model reloads
834 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
835 _marked_for_selection.insert((*i)->note());
839 _model->apply_command(*trackview.session(), _diff_command);
841 midi_view()->midi_track()->playlist_modified();
844 _marked_for_selection.clear();
847 _marked_for_velocity.clear();
851 MidiRegionView::apply_diff_as_subcommand()
855 if (!_diff_command) {
859 if ((add_or_remove = _diff_command->adds_or_removes())) {
860 // Mark all selected notes for selection when model reloads
861 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
862 _marked_for_selection.insert((*i)->note());
866 _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
868 midi_view()->midi_track()->playlist_modified();
871 _marked_for_selection.clear();
873 _marked_for_velocity.clear();
878 MidiRegionView::abort_command()
880 delete _diff_command;
886 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
888 if (_optimization_iterator != _events.end()) {
889 ++_optimization_iterator;
892 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
893 return *_optimization_iterator;
896 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
897 if ((*_optimization_iterator)->note() == note) {
898 return *_optimization_iterator;
906 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
908 MidiModel::Notes notes;
909 _model->get_notes (notes, op, val, chan_mask);
911 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
912 CanvasNoteEvent* cne = find_canvas_note (*n);
920 MidiRegionView::redisplay_model()
922 // Don't redisplay the model if we're currently recording and displaying that
928 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
932 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
936 MidiModel::ReadLock lock(_model->read_lock());
938 MidiModel::Notes& notes (_model->notes());
939 _optimization_iterator = _events.begin();
941 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
943 boost::shared_ptr<NoteType> note (*n);
944 CanvasNoteEvent* cne;
947 if (note_in_region_range (note, visible)) {
949 if ((cne = find_canvas_note (note)) != 0) {
956 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
958 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
970 add_note (note, visible);
975 if ((cne = find_canvas_note (note)) != 0) {
983 /* remove note items that are no longer valid */
985 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
986 if (!(*i)->valid ()) {
988 i = _events.erase (i);
994 _pgm_changes.clear();
998 display_program_changes();
1000 _marked_for_selection.clear ();
1001 _marked_for_velocity.clear ();
1003 /* we may have caused _events to contain things out of order (e.g. if a note
1004 moved earlier or later). we don't generally need them in time order, but
1005 make a note that a sort is required for those cases that require it.
1008 _sort_needed = true;
1012 MidiRegionView::display_program_changes()
1014 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1015 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1017 for (uint8_t i = 0; i < 16; ++i) {
1018 if (chn_mask & (1<<i)) {
1019 display_program_changes_on_channel (i);
1025 MidiRegionView::display_program_changes_on_channel(uint8_t channel)
1027 boost::shared_ptr<Evoral::Control> control =
1028 _model->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation, channel));
1034 Glib::Mutex::Lock lock (control->list()->lock());
1036 for (AutomationList::const_iterator event = control->list()->begin();
1037 event != control->list()->end(); ++event) {
1038 double event_time = (*event)->when;
1039 double program_number = floor((*event)->value + 0.5);
1041 // Get current value of bank select MSB at time of the program change
1042 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1043 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1045 if (msb_control != 0) {
1046 msb = uint8_t(floor(msb_control->get_double(true, event_time) + 0.5));
1049 // Get current value of bank select LSB at time of the program change
1050 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1051 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1053 if (lsb_control != 0) {
1054 lsb = uint8_t(floor(lsb_control->get_double(true, event_time) + 0.5));
1057 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
1059 boost::shared_ptr<MIDI::Name::Patch> patch =
1060 MIDI::Name::MidiPatchManager::instance().find_patch(
1061 _model_name, _custom_device_mode, channel, patch_key);
1063 PCEvent program_change(event_time, uint8_t(program_number), channel);
1066 add_pgm_change(program_change, patch->name());
1069 // program_number is zero-based: convert to one-based
1070 snprintf(buf, 4, "%d", int(program_number+1));
1071 add_pgm_change(program_change, buf);
1077 MidiRegionView::display_sysexes()
1079 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1080 Evoral::MusicalTime time = (*i)->time();
1085 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1086 str << int((*i)->buffer()[b]);
1087 if (b != (*i)->size() -1) {
1091 string text = str.str();
1093 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1095 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1097 double height = midi_stream_view()->contents_height();
1099 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1100 new CanvasSysEx(*this, *group, text, height, x, 1.0));
1102 // Show unless program change is beyond the region bounds
1103 if (time - _region->start() >= _region->length() || time < _region->start()) {
1109 _sys_exes.push_back(sysex);
1114 MidiRegionView::~MidiRegionView ()
1116 in_destructor = true;
1118 trackview.editor().hide_verbose_canvas_cursor ();
1120 note_delete_connection.disconnect ();
1122 delete _list_editor;
1124 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1126 if (_active_notes) {
1134 delete _diff_command;
1135 delete _step_edit_cursor;
1139 MidiRegionView::region_resized (const PropertyChange& what_changed)
1141 RegionView::region_resized(what_changed);
1143 if (what_changed.contains (ARDOUR::Properties::position)) {
1144 set_duration(_region->length(), 0);
1145 if (_enable_display) {
1152 MidiRegionView::reset_width_dependent_items (double pixel_width)
1154 RegionView::reset_width_dependent_items(pixel_width);
1155 assert(_pixel_width == pixel_width);
1157 if (_enable_display) {
1161 move_step_edit_cursor (_step_edit_cursor_position);
1162 set_step_edit_cursor_width (_step_edit_cursor_width);
1166 MidiRegionView::set_height (double height)
1168 static const double FUDGE = 2.0;
1169 const double old_height = _height;
1170 RegionView::set_height(height);
1171 _height = height - FUDGE;
1173 apply_note_range(midi_stream_view()->lowest_note(),
1174 midi_stream_view()->highest_note(),
1175 height != old_height + FUDGE);
1178 name_pixbuf->raise_to_top();
1181 for (PgmChanges::iterator x = _pgm_changes.begin(); x != _pgm_changes.end(); ++x) {
1182 (*x)->set_height (midi_stream_view()->contents_height());
1185 if (_step_edit_cursor) {
1186 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1191 /** Apply the current note range from the stream view
1192 * by repositioning/hiding notes as necessary
1195 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1197 if (!_enable_display) {
1201 if (!force && _current_range_min == min && _current_range_max == max) {
1205 _current_range_min = min;
1206 _current_range_max = max;
1208 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1209 CanvasNoteEvent* event = *i;
1210 boost::shared_ptr<NoteType> note (event->note());
1212 if (note->note() < _current_range_min ||
1213 note->note() > _current_range_max) {
1219 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1221 const double y1 = midi_stream_view()->note_to_y(note->note());
1222 const double y2 = y1 + floor(midi_stream_view()->note_height());
1224 cnote->property_y1() = y1;
1225 cnote->property_y2() = y2;
1227 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1229 double x = trackview.editor().frame_to_pixel(
1230 beats_to_frames(note->time()) - _region->start());
1231 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1232 double y = midi_stream_view()->note_to_y(event->note()->note())
1233 + ((diamond_size-2.0) / 4.0);
1235 chit->set_height (diamond_size);
1236 chit->move (x - chit->x1(), y - chit->y1());
1243 MidiRegionView::add_ghost (TimeAxisView& tv)
1247 double unit_position = _region->position () / samples_per_unit;
1248 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1249 MidiGhostRegion* ghost;
1251 if (mtv && mtv->midi_view()) {
1252 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1253 to allow having midi notes on top of note lines and waveforms.
1255 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1257 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1260 ghost->set_height ();
1261 ghost->set_duration (_region->length() / samples_per_unit);
1262 ghosts.push_back (ghost);
1264 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1265 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1266 ghost->add_note(note);
1270 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1276 /** Begin tracking note state for successive calls to add_event
1279 MidiRegionView::begin_write()
1281 assert(!_active_notes);
1282 _active_notes = new CanvasNote*[128];
1283 for (unsigned i=0; i < 128; ++i) {
1284 _active_notes[i] = 0;
1289 /** Destroy note state for add_event
1292 MidiRegionView::end_write()
1294 delete[] _active_notes;
1296 _marked_for_selection.clear();
1297 _marked_for_velocity.clear();
1301 /** Resolve an active MIDI note (while recording).
1304 MidiRegionView::resolve_note(uint8_t note, double end_time)
1306 if (midi_view()->note_mode() != Sustained) {
1310 if (_active_notes && _active_notes[note]) {
1311 const framepos_t end_time_frames = beats_to_frames(end_time) - _region->start();
1312 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1313 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1314 _active_notes[note] = 0;
1319 /** Extend active notes to rightmost edge of region (if length is changed)
1322 MidiRegionView::extend_active_notes()
1324 if (!_active_notes) {
1328 for (unsigned i=0; i < 128; ++i) {
1329 if (_active_notes[i]) {
1330 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1337 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1339 if (no_sound_notes || !trackview.editor().sound_notes()) {
1343 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1345 if (!route_ui || !route_ui->midi_track()) {
1349 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1355 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1357 if (no_sound_notes || !trackview.editor().sound_notes()) {
1361 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1363 if (!route_ui || !route_ui->midi_track()) {
1367 NotePlayer* np = new NotePlayer (route_ui->midi_track());
1369 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1378 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1380 const framepos_t note_start_frames = beats_to_frames(note->time());
1382 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1383 (note_start_frames < _region->start());
1385 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1386 (note->note() <= midi_stream_view()->highest_note());
1392 MidiRegionView::update_note (CanvasNote* ev)
1394 boost::shared_ptr<NoteType> note = ev->note();
1396 const framepos_t note_start_frames = beats_to_frames(note->time());
1398 /* trim note display to not overlap the end of its region */
1399 const framepos_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1401 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1402 const double y1 = midi_stream_view()->note_to_y(note->note());
1403 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1405 ev->property_x1() = x;
1406 ev->property_y1() = y1;
1407 if (note->length() > 0) {
1408 ev->property_x2() = note_endpixel;
1410 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1412 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1414 if (note->length() == 0) {
1415 if (_active_notes) {
1416 assert(note->note() < 128);
1417 // If this note is already active there's a stuck note,
1418 // finish the old note rectangle
1419 if (_active_notes[note->note()]) {
1420 CanvasNote* const old_rect = _active_notes[note->note()];
1421 boost::shared_ptr<NoteType> old_note = old_rect->note();
1422 old_rect->property_x2() = x;
1423 old_rect->property_outline_what() = (guint32) 0xF;
1425 _active_notes[note->note()] = ev;
1427 /* outline all but right edge */
1428 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1430 /* outline all edges */
1431 ev->property_outline_what() = (guint32) 0xF;
1436 MidiRegionView::update_hit (CanvasHit* ev)
1438 boost::shared_ptr<NoteType> note = ev->note();
1440 const framepos_t note_start_frames = beats_to_frames(note->time());
1441 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1442 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1443 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1448 /** Add a MIDI note to the view (with length).
1450 * If in sustained mode, notes with length 0 will be considered active
1451 * notes, and resolve_note should be called when the corresponding note off
1452 * event arrives, to properly display the note.
1455 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1457 CanvasNoteEvent* event = 0;
1459 assert(note->time() >= 0);
1460 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1462 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1464 if (midi_view()->note_mode() == Sustained) {
1466 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1468 update_note (ev_rect);
1472 MidiGhostRegion* gr;
1474 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1475 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1476 gr->add_note(ev_rect);
1480 } else if (midi_view()->note_mode() == Percussive) {
1482 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1484 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1486 update_hit (ev_diamond);
1495 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1496 note_selected(event, true);
1499 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1500 event->show_velocity();
1502 event->on_channel_selection_change(_last_channel_selection);
1503 _events.push_back(event);
1514 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1515 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1517 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1519 /* potentially extend region to hold new note */
1521 framepos_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1522 framepos_t region_end = _region->position() + _region->length() - 1;
1524 if (end_frame > region_end) {
1525 _region->set_length (end_frame - _region->position(), this);
1528 _marked_for_selection.clear ();
1531 start_diff_command (_("step add"));
1532 diff_add_note (new_note, true, false);
1535 // last_step_edit_note = new_note;
1539 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1541 change_note_lengths (false, false, beats, false, true);
1545 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1547 assert(program.time >= 0);
1549 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1550 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1552 double height = midi_stream_view()->contents_height();
1554 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1555 new CanvasProgramChange(*this, *group,
1560 _custom_device_mode,
1561 program.time, program.channel, program.value));
1563 // Show unless program change is beyond the region bounds
1564 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1570 _pgm_changes.push_back(pgm_change);
1574 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1576 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1577 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1579 if (msb_control != 0) {
1580 msb = int(msb_control->get_double(true, time));
1583 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1584 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1586 if (lsb_control != 0) {
1587 lsb = lsb_control->get_double(true, time);
1590 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1591 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1592 double program_number = -1.0;
1593 if (program_control != 0) {
1594 program_number = program_control->get_double(true, time);
1597 key.msb = (int) floor(msb + 0.5);
1598 key.lsb = (int) floor(lsb + 0.5);
1599 key.program_number = (int) floor(program_number + 0.5);
1600 assert(key.is_sane());
1605 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1607 // TODO: Get the real event here and alter them at the original times
1608 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1609 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1610 if (msb_control != 0) {
1611 msb_control->set_double(double(new_patch.msb), true, old_program.time);
1614 // TODO: Get the real event here and alter them at the original times
1615 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1616 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1617 if (lsb_control != 0) {
1618 lsb_control->set_double(double(new_patch.lsb), true, old_program.time);
1621 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1622 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1624 assert(program_control != 0);
1625 program_control->set_double(float(new_patch.program_number), true, old_program.time);
1627 _pgm_changes.clear ();
1628 display_program_changes (); // XXX would be nice to limit to just old_program.channel
1632 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1634 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1635 alter_program_change(program_change_event, new_patch);
1639 MidiRegionView::previous_program(CanvasProgramChange& program)
1641 if (program.program() < 127) {
1642 MIDI::Name::PatchPrimaryKey key;
1643 get_patch_key_at(program.event_time(), program.channel(), key);
1644 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1646 key.program_number++;
1647 alter_program_change(program_change_event, key);
1652 MidiRegionView::next_program(CanvasProgramChange& program)
1654 if (program.program() > 0) {
1655 MIDI::Name::PatchPrimaryKey key;
1656 get_patch_key_at(program.event_time(), program.channel(), key);
1657 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1659 key.program_number--;
1660 alter_program_change(program_change_event, key);
1665 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1667 if (_selection.empty()) {
1671 if (_selection.erase (cne) > 0) {
1672 cerr << "Erased a CNE from selection\n";
1677 MidiRegionView::delete_selection()
1679 if (_selection.empty()) {
1683 start_diff_command (_("delete selection"));
1685 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1686 if ((*i)->selected()) {
1687 _diff_command->remove((*i)->note());
1697 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1699 start_diff_command (_("delete note"));
1700 _diff_command->remove (n);
1703 trackview.editor().hide_verbose_canvas_cursor ();
1707 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1709 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1710 if ((*i)->selected() && (*i) != ev) {
1711 (*i)->set_selected(false);
1712 (*i)->hide_velocity();
1720 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1722 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1725 Selection::iterator tmp = i;
1728 (*i)->set_selected (false);
1729 _selection.erase (i);
1738 /* don't bother with removing this regionview from the editor selection,
1739 since we're about to add another note, and thus put/keep this
1740 regionview in the editor selection.
1743 if (!ev->selected()) {
1744 add_to_selection (ev);
1749 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1751 uint8_t low_note = 127;
1752 uint8_t high_note = 0;
1753 MidiModel::Notes& notes (_model->notes());
1754 _optimization_iterator = _events.begin();
1760 if (extend && _selection.empty()) {
1766 /* scan existing selection to get note range */
1768 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1769 if ((*i)->note()->note() < low_note) {
1770 low_note = (*i)->note()->note();
1772 if ((*i)->note()->note() > high_note) {
1773 high_note = (*i)->note()->note();
1777 low_note = min (low_note, notenum);
1778 high_note = max (high_note, notenum);
1781 no_sound_notes = true;
1783 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1785 boost::shared_ptr<NoteType> note (*n);
1786 CanvasNoteEvent* cne;
1787 bool select = false;
1789 if (((1 << note->channel()) & channel_mask) != 0) {
1791 if ((note->note() >= low_note && note->note() <= high_note)) {
1794 } else if (note->note() == notenum) {
1800 if ((cne = find_canvas_note (note)) != 0) {
1801 // extend is false because we've taken care of it,
1802 // since it extends by time range, not pitch.
1803 note_selected (cne, add, false);
1807 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1811 no_sound_notes = false;
1815 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1817 MidiModel::Notes& notes (_model->notes());
1818 _optimization_iterator = _events.begin();
1820 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1822 boost::shared_ptr<NoteType> note (*n);
1823 CanvasNoteEvent* cne;
1825 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1826 if ((cne = find_canvas_note (note)) != 0) {
1827 if (cne->selected()) {
1828 note_deselected (cne);
1830 note_selected (cne, true, false);
1838 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1841 clear_selection_except(ev);
1846 if (!ev->selected()) {
1847 add_to_selection (ev);
1851 /* find end of latest note selected, select all between that and the start of "ev" */
1853 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1854 Evoral::MusicalTime latest = 0;
1856 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1857 if ((*i)->note()->end_time() > latest) {
1858 latest = (*i)->note()->end_time();
1860 if ((*i)->note()->time() < earliest) {
1861 earliest = (*i)->note()->time();
1865 if (ev->note()->end_time() > latest) {
1866 latest = ev->note()->end_time();
1869 if (ev->note()->time() < earliest) {
1870 earliest = ev->note()->time();
1873 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1875 /* find notes entirely within OR spanning the earliest..latest range */
1877 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1878 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1879 add_to_selection (*i);
1883 /* if events were guaranteed to be time sorted, we could do this.
1884 but as of sept 10th 2009, they no longer are.
1887 if ((*i)->note()->time() > latest) {
1896 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1898 remove_from_selection (ev);
1902 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1912 // TODO: Make this faster by storing the last updated selection rect, and only
1913 // adjusting things that are in the area that appears/disappeared.
1914 // We probably need a tree to be able to find events in O(log(n)) time.
1916 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1918 /* check if any corner of the note is inside the rect
1921 1) this is computing "touched by", not "contained by" the rect.
1922 2) this does not require that events be sorted in time.
1925 const double ix1 = (*i)->x1();
1926 const double ix2 = (*i)->x2();
1927 const double iy1 = (*i)->y1();
1928 const double iy2 = (*i)->y2();
1930 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1931 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1932 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1933 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1936 if (!(*i)->selected()) {
1937 add_to_selection (*i);
1939 } else if ((*i)->selected()) {
1940 // Not inside rectangle
1941 remove_from_selection (*i);
1947 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1949 Selection::iterator i = _selection.find (ev);
1951 if (i != _selection.end()) {
1952 _selection.erase (i);
1955 ev->set_selected (false);
1956 ev->hide_velocity ();
1958 if (_selection.empty()) {
1959 PublicEditor& editor (trackview.editor());
1960 editor.get_selection().remove (this);
1965 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
1967 bool add_mrv_selection = false;
1969 if (_selection.empty()) {
1970 add_mrv_selection = true;
1973 if (_selection.insert (ev).second) {
1974 ev->set_selected (true);
1975 play_midi_note ((ev)->note());
1978 if (add_mrv_selection) {
1979 PublicEditor& editor (trackview.editor());
1980 editor.get_selection().add (this);
1985 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
1987 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
1988 PossibleChord to_play;
1989 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
1991 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1992 if ((*i)->note()->time() < earliest) {
1993 earliest = (*i)->note()->time();
1997 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1998 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
1999 to_play.push_back ((*i)->note());
2001 (*i)->move_event(dx, dy);
2004 if (dy && !_selection.empty() && !no_sound_notes && trackview.editor().sound_notes()) {
2006 if (to_play.size() > 1) {
2008 PossibleChord shifted;
2010 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2011 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2012 moved_note->set_note (moved_note->note() + cumulative_dy);
2013 shifted.push_back (moved_note);
2016 play_midi_chord (shifted);
2018 } else if (!to_play.empty()) {
2020 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2021 moved_note->set_note (moved_note->note() + cumulative_dy);
2022 play_midi_note (moved_note);
2028 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2030 assert (!_selection.empty());
2032 uint8_t lowest_note_in_selection = 127;
2033 uint8_t highest_note_in_selection = 0;
2034 uint8_t highest_note_difference = 0;
2036 // find highest and lowest notes first
2038 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2039 uint8_t pitch = (*i)->note()->note();
2040 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2041 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2045 cerr << "dnote: " << (int) dnote << endl;
2046 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2047 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2048 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2049 << int(highest_note_in_selection) << endl;
2050 cerr << "selection size: " << _selection.size() << endl;
2051 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2054 // Make sure the note pitch does not exceed the MIDI standard range
2055 if (highest_note_in_selection + dnote > 127) {
2056 highest_note_difference = highest_note_in_selection - 127;
2059 start_diff_command(_("move notes"));
2061 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2063 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2069 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
2071 uint8_t original_pitch = (*i)->note()->note();
2072 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2074 // keep notes in standard midi range
2075 clamp_to_0_127(new_pitch);
2077 // keep original pitch if note is dragged outside valid midi range
2078 if ((original_pitch != 0 && new_pitch == 0)
2079 || (original_pitch != 127 && new_pitch == 127)) {
2080 new_pitch = original_pitch;
2083 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2084 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2086 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
2091 // care about notes being moved beyond the upper/lower bounds on the canvas
2092 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2093 highest_note_in_selection > midi_stream_view()->highest_note()) {
2094 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2099 MidiRegionView::snap_pixel_to_frame(double x)
2101 PublicEditor& editor = trackview.editor();
2102 // x is region relative, convert it to global absolute frames
2103 framepos_t frame = editor.pixel_to_frame(x) + _region->position();
2104 editor.snap_to(frame);
2105 return frame - _region->position(); // convert back to region relative
2109 MidiRegionView::snap_frame_to_frame(framepos_t x)
2111 PublicEditor& editor = trackview.editor();
2112 // x is region relative, convert it to global absolute frames
2113 framepos_t frame = x + _region->position();
2114 editor.snap_to(frame);
2115 return frame - _region->position(); // convert back to region relative
2119 MidiRegionView::snap_to_pixel(double x)
2121 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2125 MidiRegionView::get_position_pixels()
2127 framepos_t region_frame = get_position();
2128 return trackview.editor().frame_to_pixel(region_frame);
2132 MidiRegionView::get_end_position_pixels()
2134 framepos_t frame = get_position() + get_duration ();
2135 return trackview.editor().frame_to_pixel(frame);
2139 MidiRegionView::beats_to_frames(double beats) const
2141 return _time_converter.to(beats);
2145 MidiRegionView::frames_to_beats(framepos_t frames) const
2147 return _time_converter.from(frames);
2151 MidiRegionView::begin_resizing (bool /*at_front*/)
2153 _resize_data.clear();
2155 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2156 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2158 // only insert CanvasNotes into the map
2160 NoteResizeData *resize_data = new NoteResizeData();
2161 resize_data->canvas_note = note;
2163 // create a new SimpleRect from the note which will be the resize preview
2164 SimpleRect *resize_rect = new SimpleRect(
2165 *group, note->x1(), note->y1(), note->x2(), note->y2());
2167 // calculate the colors: get the color settings
2168 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2169 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2172 // make the resize preview notes more transparent and bright
2173 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2175 // calculate color based on note velocity
2176 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2177 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2181 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2182 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2184 resize_data->resize_rect = resize_rect;
2185 _resize_data.push_back(resize_data);
2190 /** Update resizing notes while user drags.
2191 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2192 * @param at_front which end of the note (true == note on, false == note off)
2193 * @param delta_x change in mouse position since the start of the drag
2194 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2195 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2196 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2197 * as the \a primary note.
2200 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2202 bool cursor_set = false;
2204 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2205 SimpleRect* resize_rect = (*i)->resize_rect;
2206 CanvasNote* canvas_note = (*i)->canvas_note;
2211 current_x = canvas_note->x1() + delta_x;
2213 current_x = primary->x1() + delta_x;
2217 current_x = canvas_note->x2() + delta_x;
2219 current_x = primary->x2() + delta_x;
2224 resize_rect->property_x1() = snap_to_pixel(current_x);
2225 resize_rect->property_x2() = canvas_note->x2();
2227 resize_rect->property_x2() = snap_to_pixel(current_x);
2228 resize_rect->property_x1() = canvas_note->x1();
2234 beats = snap_pixel_to_frame (current_x);
2235 beats = frames_to_beats (beats);
2240 if (beats < canvas_note->note()->end_time()) {
2241 len = canvas_note->note()->time() - beats;
2242 len += canvas_note->note()->length();
2247 if (beats >= canvas_note->note()->time()) {
2248 len = beats - canvas_note->note()->time();
2255 snprintf (buf, sizeof (buf), "%.3g beats", len);
2256 trackview.editor().show_verbose_canvas_cursor_with (buf);
2265 /** Finish resizing notes when the user releases the mouse button.
2266 * Parameters the same as for \a update_resizing().
2269 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2271 start_diff_command(_("resize notes"));
2273 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2274 CanvasNote* canvas_note = (*i)->canvas_note;
2275 SimpleRect* resize_rect = (*i)->resize_rect;
2280 current_x = canvas_note->x1() + delta_x;
2282 current_x = primary->x1() + delta_x;
2286 current_x = canvas_note->x2() + delta_x;
2288 current_x = primary->x2() + delta_x;
2292 current_x = snap_pixel_to_frame (current_x);
2293 current_x = frames_to_beats (current_x);
2295 if (at_front && current_x < canvas_note->note()->end_time()) {
2296 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2298 double len = canvas_note->note()->time() - current_x;
2299 len += canvas_note->note()->length();
2302 /* XXX convert to beats */
2303 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2308 double len = current_x - canvas_note->note()->time();
2311 /* XXX convert to beats */
2312 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2320 _resize_data.clear();
2325 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2327 diff_add_change (event, MidiModel::DiffCommand::Channel, (uint8_t) channel);
2331 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2333 uint8_t new_velocity;
2336 new_velocity = event->note()->velocity() + velocity;
2337 clamp_to_0_127(new_velocity);
2339 new_velocity = velocity;
2342 event->set_selected (event->selected()); // change color
2344 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2348 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2353 new_note = event->note()->note() + note;
2358 clamp_to_0_127 (new_note);
2359 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2363 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2365 bool change_start = false;
2366 bool change_length = false;
2367 Evoral::MusicalTime new_start = 0;
2368 Evoral::MusicalTime new_length = 0;
2370 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2372 front_delta: if positive - move the start of the note later in time (shortening it)
2373 if negative - move the start of the note earlier in time (lengthening it)
2375 end_delta: if positive - move the end of the note later in time (lengthening it)
2376 if negative - move the end of the note earlier in time (shortening it)
2380 if (front_delta < 0) {
2382 if (event->note()->time() < -front_delta) {
2385 new_start = event->note()->time() + front_delta; // moves earlier
2388 /* start moved toward zero, so move the end point out to where it used to be.
2389 Note that front_delta is negative, so this increases the length.
2392 new_length = event->note()->length() - front_delta;
2393 change_start = true;
2394 change_length = true;
2398 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2400 if (new_pos < event->note()->end_time()) {
2401 new_start = event->note()->time() + front_delta;
2402 /* start moved toward the end, so move the end point back to where it used to be */
2403 new_length = event->note()->length() - front_delta;
2404 change_start = true;
2405 change_length = true;
2412 bool can_change = true;
2413 if (end_delta < 0) {
2414 if (event->note()->length() < -end_delta) {
2420 new_length = event->note()->length() + end_delta;
2421 change_length = true;
2426 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2429 if (change_length) {
2430 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2435 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2437 Evoral::MusicalTime new_time;
2441 if (event->note()->time() < -delta) {
2444 new_time = event->note()->time() + delta;
2447 new_time = event->note()->time() + delta;
2453 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2457 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2459 diff_add_change (event, MidiModel::DiffCommand::Length, t);
2463 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2467 if (_selection.empty()) {
2482 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2483 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2489 start_diff_command(_("change velocities"));
2491 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2492 Selection::iterator next = i;
2494 change_note_velocity (*i, delta, true);
2500 if (!_selection.empty()) {
2502 snprintf (buf, sizeof (buf), "Vel %d",
2503 (int) (*_selection.begin())->note()->velocity());
2504 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 10);
2510 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2512 if (_selection.empty()) {
2529 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2531 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2535 if ((int8_t) (*i)->note()->note() + delta > 127) {
2542 start_diff_command (_("transpose"));
2544 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2545 Selection::iterator next = i;
2547 change_note_note (*i, delta, true);
2555 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2561 /* grab the current grid distance */
2563 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2565 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2566 cerr << "Grid type not available as beats - TO BE FIXED\n";
2576 start_diff_command (_("change note lengths"));
2578 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2579 Selection::iterator next = i;
2582 /* note the negation of the delta for start */
2584 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2593 MidiRegionView::nudge_notes (bool forward)
2595 if (_selection.empty()) {
2599 /* pick a note as the point along the timeline to get the nudge distance.
2600 its not necessarily the earliest note, so we may want to pull the notes out
2601 into a vector and sort before using the first one.
2604 framepos_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2606 framepos_t distance;
2608 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2610 /* grid is off - use nudge distance */
2612 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2618 framepos_t next_pos = ref_point;
2621 if (max_framepos - 1 < next_pos) {
2625 if (next_pos == 0) {
2631 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2632 distance = ref_point - next_pos;
2635 if (distance == 0) {
2639 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2645 start_diff_command (_("nudge"));
2647 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2648 Selection::iterator next = i;
2650 change_note_time (*i, delta, true);
2658 MidiRegionView::change_channel(uint8_t channel)
2660 start_diff_command(_("change channel"));
2661 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2662 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2670 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2672 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2674 pre_enter_cursor = editor->get_canvas_cursor ();
2676 if (_mouse_state == SelectTouchDragging) {
2677 note_selected (ev, true);
2680 show_verbose_canvas_cursor (ev->note ());
2684 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent* note)
2686 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2688 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2689 (*i)->hide_velocity ();
2692 editor->hide_verbose_canvas_cursor ();
2694 if (pre_enter_cursor) {
2695 editor->set_canvas_cursor (pre_enter_cursor);
2696 pre_enter_cursor = 0;
2701 MidiRegionView::note_mouse_position (float x_fraction, float y_fraction, bool can_set_cursor)
2703 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2705 if (x_fraction > 0.0 && x_fraction < 0.25) {
2706 editor->set_canvas_cursor (editor->left_side_trim_cursor);
2707 } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
2708 editor->set_canvas_cursor (editor->right_side_trim_cursor);
2710 if (pre_enter_cursor && can_set_cursor) {
2711 editor->set_canvas_cursor (pre_enter_cursor);
2717 MidiRegionView::set_frame_color()
2720 if (_selected && should_show_selection) {
2721 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2723 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2729 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2733 case FilterChannels:
2734 _force_channel = -1;
2737 _force_channel = mask;
2738 mask = 0xFFFF; // Show all notes as active (below)
2741 // Update notes for selection
2742 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2743 (*i)->on_channel_selection_change(mask);
2746 _last_channel_selection = mask;
2750 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2752 _model_name = model;
2753 _custom_device_mode = custom_device_mode;
2758 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2760 if (_selection.empty()) {
2764 PublicEditor& editor (trackview.editor());
2769 editor.get_cut_buffer().add (selection_as_cut_buffer());
2777 start_diff_command();
2779 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2785 diff_remove_note (*i);
2795 MidiRegionView::selection_as_cut_buffer () const
2799 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2800 NoteType* n = (*i)->note().get();
2801 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2804 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2811 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
2817 start_diff_command (_("paste"));
2819 Evoral::MusicalTime beat_delta;
2820 Evoral::MusicalTime paste_pos_beats;
2821 Evoral::MusicalTime duration;
2822 Evoral::MusicalTime end_point = 0;
2824 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2825 paste_pos_beats = frames_to_beats (pos - _region->position());
2826 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2827 paste_pos_beats = 0;
2831 for (int n = 0; n < (int) times; ++n) {
2833 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2835 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2836 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2838 /* make all newly added notes selected */
2840 diff_add_note (copied_note, true);
2841 end_point = copied_note->end_time();
2844 paste_pos_beats += duration;
2847 /* if we pasted past the current end of the region, extend the region */
2849 framepos_t end_frame = _region->position() + beats_to_frames (end_point);
2850 framepos_t region_end = _region->position() + _region->length() - 1;
2852 if (end_frame > region_end) {
2854 trackview.session()->begin_reversible_command (_("paste"));
2856 _region->clear_changes ();
2857 _region->set_length (end_frame, this);
2858 trackview.session()->add_command (new StatefulDiffCommand (_region));
2864 struct EventNoteTimeEarlyFirstComparator {
2865 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2866 return a->note()->time() < b->note()->time();
2871 MidiRegionView::time_sort_events ()
2873 if (!_sort_needed) {
2877 EventNoteTimeEarlyFirstComparator cmp;
2880 _sort_needed = false;
2884 MidiRegionView::goto_next_note ()
2886 // framepos_t pos = -1;
2887 bool use_next = false;
2889 if (_events.back()->selected()) {
2893 time_sort_events ();
2895 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2896 if ((*i)->selected()) {
2899 } else if (use_next) {
2901 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2906 /* use the first one */
2908 unique_select (_events.front());
2913 MidiRegionView::goto_previous_note ()
2915 // framepos_t pos = -1;
2916 bool use_next = false;
2918 if (_events.front()->selected()) {
2922 time_sort_events ();
2924 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2925 if ((*i)->selected()) {
2928 } else if (use_next) {
2930 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2935 /* use the last one */
2937 unique_select (*(_events.rbegin()));
2941 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2943 bool had_selected = false;
2945 time_sort_events ();
2947 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2948 if ((*i)->selected()) {
2949 selected.insert ((*i)->note());
2950 had_selected = true;
2954 if (allow_all_if_none_selected && !had_selected) {
2955 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2956 selected.insert ((*i)->note());
2962 MidiRegionView::update_ghost_note (double x, double y)
2968 framepos_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
2969 trackview.editor().snap_to (f);
2970 f -= _region->position ();
2973 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
2978 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
2980 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
2981 _ghost_note->note()->set_length (length);
2982 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
2984 update_note (_ghost_note);
2986 show_verbose_canvas_cursor (_ghost_note->note ());
2990 MidiRegionView::create_ghost_note (double x, double y)
2995 boost::shared_ptr<NoteType> g (new NoteType);
2996 _ghost_note = new NoEventCanvasNote (*this, *group, g);
2997 update_ghost_note (x, y);
2998 _ghost_note->show ();
3003 show_verbose_canvas_cursor (_ghost_note->note ());
3007 MidiRegionView::snap_changed ()
3013 create_ghost_note (_last_ghost_x, _last_ghost_y);
3017 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
3020 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
3021 Evoral::midi_note_name (n->note()).c_str(),
3023 (int) n->velocity());
3024 trackview.editor().show_verbose_canvas_cursor_with (buf, 10, 20);
3028 MidiRegionView::drop_down_keys ()
3030 _mouse_state = None;
3034 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double x, double y)
3036 double note = midi_stream_view()->y_to_note(y);
3038 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3040 cerr << "Selecting by position\n";
3042 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3044 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3045 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3046 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3047 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3052 bool add_mrv_selection = false;
3054 if (_selection.empty()) {
3055 add_mrv_selection = true;
3058 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3059 if (_selection.insert (*i).second) {
3060 (*i)->set_selected (true);
3064 if (add_mrv_selection) {
3065 PublicEditor& editor (trackview.editor());
3066 editor.get_selection().add (this);
3071 MidiRegionView::color_handler ()
3073 RegionView::color_handler ();
3075 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3076 (*i)->set_selected ((*i)->selected()); // will change color
3079 /* XXX probably more to do here */
3083 MidiRegionView::enable_display (bool yn)
3085 RegionView::enable_display (yn);
3092 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3094 if (_step_edit_cursor == 0) {
3095 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3097 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3098 _step_edit_cursor->property_y1() = 0;
3099 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3100 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3101 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3104 move_step_edit_cursor (pos);
3105 _step_edit_cursor->show ();
3109 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3111 _step_edit_cursor_position = pos;
3113 if (_step_edit_cursor) {
3114 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3115 _step_edit_cursor->property_x1() = pixel;
3116 set_step_edit_cursor_width (_step_edit_cursor_width);
3121 MidiRegionView::hide_step_edit_cursor ()
3123 if (_step_edit_cursor) {
3124 _step_edit_cursor->hide ();
3129 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3131 _step_edit_cursor_width = beats;
3133 if (_step_edit_cursor) {
3134 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));
3138 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3139 * @param buf Data that has been recorded.
3140 * @param w Source that this data will end up in.
3143 MidiRegionView::data_recorded (boost::shared_ptr<MidiBuffer> buf, boost::weak_ptr<MidiSource> w)
3145 if (!_active_notes) {
3146 /* we aren't actively being recorded to */
3150 boost::shared_ptr<MidiSource> src = w.lock ();
3151 if (!src || src != midi_region()->midi_source()) {
3152 /* recorded data was not destined for our source */
3156 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3157 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3159 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3160 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3161 assert (ev.buffer ());
3163 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3165 if (ev.type() == MIDI_CMD_NOTE_ON) {
3167 boost::shared_ptr<Evoral::Note<Evoral::MusicalTime> > note (
3168 new Evoral::Note<Evoral::MusicalTime> (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3171 add_note (note, true);
3173 /* fix up our note range */
3174 if (ev.note() < _current_range_min) {
3175 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3176 } else if (ev.note() > _current_range_max) {
3177 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3180 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3181 resolve_note (ev.note (), time_beats);