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/Control.hpp"
44 #include "evoral/midi_util.h"
46 #include "automation_region_view.h"
47 #include "automation_time_axis.h"
48 #include "canvas-hit.h"
49 #include "canvas-note.h"
50 #include "canvas-program-change.h"
51 #include "ghostregion.h"
52 #include "gui_thread.h"
54 #include "midi_cut_buffer.h"
55 #include "midi_list_editor.h"
56 #include "midi_region_view.h"
57 #include "midi_streamview.h"
58 #include "midi_time_axis.h"
59 #include "midi_time_axis.h"
60 #include "midi_util.h"
61 #include "public_editor.h"
62 #include "selection.h"
63 #include "simpleline.h"
64 #include "streamview.h"
69 using namespace ARDOUR;
71 using namespace Editing;
72 using namespace ArdourCanvas;
73 using Gtkmm2ext::Keyboard;
75 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
76 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
77 : RegionView (parent, tv, r, spu, basic_color)
79 , _last_channel_selection(0xFFFF)
80 , _current_range_min(0)
81 , _current_range_max(0)
82 , _model_name(string())
83 , _custom_device_mode(string())
85 , _note_group(new ArdourCanvas::Group(*parent))
93 , _optimization_iterator (_events.end())
95 , no_sound_notes (false)
97 _note_group->raise_to_top();
100 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
101 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
102 TimeAxisViewItem::Visibility visibility)
103 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
105 , _last_channel_selection(0xFFFF)
106 , _model_name(string())
107 , _custom_device_mode(string())
109 , _note_group(new ArdourCanvas::Group(*parent))
116 , _sort_needed (true)
117 , _optimization_iterator (_events.end())
119 , no_sound_notes (false)
121 _note_group->raise_to_top();
125 MidiRegionView::MidiRegionView (const MidiRegionView& other)
126 : sigc::trackable(other)
129 , _last_channel_selection(0xFFFF)
130 , _model_name(string())
131 , _custom_device_mode(string())
133 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
140 , _sort_needed (true)
141 , _optimization_iterator (_events.end())
143 , no_sound_notes (false)
148 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
149 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
154 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
155 : RegionView (other, boost::shared_ptr<Region> (region))
157 , _last_channel_selection(0xFFFF)
158 , _model_name(string())
159 , _custom_device_mode(string())
161 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
168 , _sort_needed (true)
169 , _optimization_iterator (_events.end())
171 , no_sound_notes (false)
176 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
177 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
183 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
185 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
186 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
190 midi_region()->midi_source(0)->load_model();
193 _model = midi_region()->midi_source(0)->model();
194 _enable_display = false;
196 RegionView::init (basic_color, false);
198 compute_colors (basic_color);
200 set_height (trackview.current_height());
203 region_sync_changed ();
204 region_resized (ARDOUR::bounds_change);
207 reset_width_dependent_items (_pixel_width);
211 _enable_display = true;
214 display_model (_model);
218 group->raise_to_top();
219 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
221 midi_view()->signal_channel_mode_changed().connect(
222 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
224 midi_view()->signal_midi_patch_settings_changed().connect(
225 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
227 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
231 MidiRegionView::canvas_event(GdkEvent* ev)
233 if (!trackview.editor().internal_editing()) {
237 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
238 to its items, which means that ev->type == GDK_SCROLL will never be seen
243 return scroll (&ev->scroll);
246 return key_press (&ev->key);
248 case GDK_KEY_RELEASE:
249 return key_release (&ev->key);
251 case GDK_BUTTON_PRESS:
252 return button_press (&ev->button);
254 case GDK_2BUTTON_PRESS:
257 case GDK_BUTTON_RELEASE:
258 return button_release (&ev->button);
260 case GDK_ENTER_NOTIFY:
261 return enter_notify (&ev->crossing);
263 case GDK_LEAVE_NOTIFY:
264 return leave_notify (&ev->crossing);
266 case GDK_MOTION_NOTIFY:
267 return motion (&ev->motion);
277 MidiRegionView::enter_notify (GdkEventCrossing* ev)
279 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
281 Keyboard::magic_widget_grab_focus();
284 if (trackview.editor().current_mouse_mode() == MouseRange) {
285 create_ghost_note (ev->x, ev->y);
292 MidiRegionView::leave_notify (GdkEventCrossing* ev)
294 trackview.editor().hide_verbose_canvas_cursor ();
301 MidiRegionView::button_press (GdkEventButton* ev)
305 group->w2i (_last_x, _last_y);
307 if (_mouse_state != SelectTouchDragging && ev->button == 1) {
308 _pressed_button = ev->button;
309 _mouse_state = Pressed;
312 _pressed_button = ev->button;
318 MidiRegionView::button_release (GdkEventButton* ev)
320 double event_x, event_y;
321 nframes64_t event_frame = 0;
325 group->w2i(event_x, event_y);
326 group->ungrab(ev->time);
327 event_frame = trackview.editor().pixel_to_frame(event_x);
329 if (ev->button == 3) {
331 } else if (_pressed_button != 1) {
335 switch (_mouse_state) {
336 case Pressed: // Clicked
337 switch (trackview.editor().current_mouse_mode()) {
345 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
350 create_note_at (event_x, event_y, beats);
358 case SelectRectDragging: // Select drag done
364 case AddDragging: // Add drag done
366 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
367 const double x = _drag_rect->property_x1();
368 const double length = trackview.editor().pixel_to_frame
369 (_drag_rect->property_x2() - _drag_rect->property_x1());
371 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length));
377 create_ghost_note (ev->x, ev->y);
387 MidiRegionView::motion (GdkEventMotion* ev)
389 double event_x, event_y;
390 nframes64_t event_frame = 0;
394 group->w2i(event_x, event_y);
396 // convert event_x to global frame
397 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
398 trackview.editor().snap_to(event_frame);
399 // convert event_frame back to local coordinates relative to position
400 event_frame -= _region->position();
403 update_ghost_note (ev->x, ev->y);
406 /* any motion immediately hides velocity text that may have been visible */
408 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
409 (*i)->hide_velocity ();
412 switch (_mouse_state) {
413 case Pressed: // Maybe start a drag, if we've moved a bit
415 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
416 /* no appreciable movement since the button was pressed */
421 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
422 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
423 Gdk::Cursor(Gdk::FLEUR), ev->time);
426 _drag_start_x = event_x;
427 _drag_start_y = event_y;
429 _drag_rect = new ArdourCanvas::SimpleRect(*group);
430 _drag_rect->property_x1() = event_x;
431 _drag_rect->property_y1() = event_y;
432 _drag_rect->property_x2() = event_x;
433 _drag_rect->property_y2() = event_y;
434 _drag_rect->property_outline_what() = 0xFF;
435 _drag_rect->property_outline_color_rgba()
436 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
437 _drag_rect->property_fill_color_rgba()
438 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
440 _mouse_state = SelectRectDragging;
443 // Add note drag start
444 } else if (trackview.editor().internal_editing()) {
449 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
450 Gdk::Cursor(Gdk::FLEUR), ev->time);
453 _drag_start_x = event_x;
454 _drag_start_y = event_y;
456 _drag_rect = new ArdourCanvas::SimpleRect(*group);
457 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
459 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
460 midi_stream_view()->y_to_note(event_y));
461 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
462 _drag_rect->property_y2() = _drag_rect->property_y1()
463 + floor(midi_stream_view()->note_height());
464 _drag_rect->property_outline_what() = 0xFF;
465 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
466 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
468 _mouse_state = AddDragging;
474 case SelectRectDragging: // Select drag motion
475 case AddDragging: // Add note drag motion
479 GdkModifierType state;
480 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
485 if (_mouse_state == AddDragging)
486 event_x = trackview.editor().frame_to_pixel(event_frame);
489 if (event_x > _drag_start_x)
490 _drag_rect->property_x2() = event_x;
492 _drag_rect->property_x1() = event_x;
495 if (_drag_rect && _mouse_state == SelectRectDragging) {
496 if (event_y > _drag_start_y)
497 _drag_rect->property_y2() = event_y;
499 _drag_rect->property_y1() = event_y;
501 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
507 case SelectTouchDragging:
519 MidiRegionView::scroll (GdkEventScroll* ev)
521 if (_selection.empty()) {
525 trackview.editor().hide_verbose_canvas_cursor ();
527 bool fine = Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
529 if (ev->direction == GDK_SCROLL_UP) {
530 change_velocities (true, fine, false);
531 } else if (ev->direction == GDK_SCROLL_DOWN) {
532 change_velocities (false, fine, false);
538 MidiRegionView::key_press (GdkEventKey* ev)
540 /* since GTK bindings are generally activated on press, and since
541 detectable auto-repeat is the name of the game and only sends
542 repeated presses, carry out key actions at key press, not release.
545 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
546 _mouse_state = SelectTouchDragging;
549 } else if (ev->keyval == GDK_Escape) {
553 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
555 bool start = (ev->keyval == GDK_comma);
556 bool end = (ev->keyval == GDK_period);
557 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
558 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
560 change_note_lengths (fine, shorter, start, end);
564 } else if (ev->keyval == GDK_Delete) {
569 } else if (ev->keyval == GDK_Tab) {
571 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
572 goto_previous_note ();
578 } else if (ev->keyval == GDK_Up) {
580 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
581 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
583 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
584 change_velocities (true, fine, allow_smush);
586 transpose (true, fine, allow_smush);
590 } else if (ev->keyval == GDK_Down) {
592 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
593 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
595 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
596 change_velocities (false, fine, allow_smush);
598 transpose (false, fine, allow_smush);
602 } else if (ev->keyval == GDK_Left) {
607 } else if (ev->keyval == GDK_Right) {
612 } else if (ev->keyval == GDK_Control_L) {
615 } else if (ev->keyval == GDK_r) {
616 /* if we're not step editing, this really doesn't matter */
617 midi_view()->step_edit_rest ();
625 MidiRegionView::key_release (GdkEventKey* ev)
627 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
635 MidiRegionView::show_list_editor ()
638 _list_editor = new MidiListEditor (trackview.session(), midi_region());
640 _list_editor->present ();
643 /** Add a note to the model, and the view, at a canvas (click) coordinate.
644 * \param x horizontal position in pixels
645 * \param y vertical position in pixels
646 * \param length duration of the note in beats */
648 MidiRegionView::create_note_at(double x, double y, double length)
650 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
651 MidiStreamView* const view = mtv->midi_view();
653 double note = midi_stream_view()->y_to_note(y);
656 assert(note <= 127.0);
658 // Start of note in frames relative to region start
659 nframes64_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
660 assert(start_frames >= 0);
663 length = frames_to_beats(
664 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
666 assert (length != 0);
668 const boost::shared_ptr<NoteType> new_note(new NoteType(0,
669 frames_to_beats(start_frames + _region->start()), length,
670 (uint8_t)note, 0x40));
672 if (_model->contains (new_note)) {
676 view->update_note_range(new_note->note());
678 MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
680 _model->apply_command(*trackview.session(), cmd);
682 play_midi_note (new_note);
686 MidiRegionView::clear_events()
691 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
692 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
697 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
702 _pgm_changes.clear();
704 _optimization_iterator = _events.end();
709 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
712 content_connection.disconnect ();
713 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
717 if (_enable_display) {
724 MidiRegionView::start_delta_command(string name)
726 if (!_delta_command) {
727 _delta_command = _model->new_delta_command(name);
732 MidiRegionView::start_diff_command(string name)
734 if (!_diff_command) {
735 _diff_command = _model->new_diff_command(name);
740 MidiRegionView::delta_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
742 if (_delta_command) {
743 _delta_command->add(note);
746 _marked_for_selection.insert(note);
749 _marked_for_velocity.insert(note);
754 MidiRegionView::delta_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
756 if (_delta_command && ev->note()) {
757 _delta_command->remove(ev->note());
762 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
763 MidiModel::DiffCommand::Property property,
767 _diff_command->change (ev->note(), property, val);
772 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
773 MidiModel::DiffCommand::Property property,
774 Evoral::MusicalTime val)
777 _diff_command->change (ev->note(), property, val);
782 MidiRegionView::apply_delta()
784 if (!_delta_command) {
788 // Mark all selected notes for selection when model reloads
789 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
790 _marked_for_selection.insert((*i)->note());
793 _model->apply_command(*trackview.session(), _delta_command);
795 midi_view()->midi_track()->playlist_modified();
797 _marked_for_selection.clear();
798 _marked_for_velocity.clear();
802 MidiRegionView::apply_diff ()
804 if (!_diff_command) {
808 _model->apply_command(*trackview.session(), _diff_command);
810 midi_view()->midi_track()->playlist_modified();
812 _marked_for_velocity.clear();
816 MidiRegionView::apply_delta_as_subcommand()
818 if (!_delta_command) {
822 // Mark all selected notes for selection when model reloads
823 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
824 _marked_for_selection.insert((*i)->note());
827 _model->apply_command_as_subcommand(*trackview.session(), _delta_command);
829 midi_view()->midi_track()->playlist_modified();
831 _marked_for_selection.clear();
832 _marked_for_velocity.clear();
836 MidiRegionView::apply_diff_as_subcommand()
838 if (!_diff_command) {
842 // Mark all selected notes for selection when model reloads
843 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
844 _marked_for_selection.insert((*i)->note());
847 _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
849 midi_view()->midi_track()->playlist_modified();
851 _marked_for_selection.clear();
852 _marked_for_velocity.clear();
856 MidiRegionView::abort_command()
858 delete _delta_command;
860 delete _diff_command;
866 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
868 if (_optimization_iterator != _events.end()) {
869 ++_optimization_iterator;
872 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
873 return *_optimization_iterator;
876 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
877 if ((*_optimization_iterator)->note() == note) {
878 return *_optimization_iterator;
886 MidiRegionView::redisplay_model()
888 // Don't redisplay the model if we're currently recording and displaying that
894 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
898 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
902 MidiModel::ReadLock lock(_model->read_lock());
904 MidiModel::Notes& notes (_model->notes());
905 _optimization_iterator = _events.begin();
907 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
909 boost::shared_ptr<NoteType> note (*n);
910 CanvasNoteEvent* cne;
913 if (note_in_region_range (note, visible)) {
915 if ((cne = find_canvas_note (note)) != 0) {
922 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
924 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
936 add_note (note, visible);
941 if ((cne = find_canvas_note (note)) != 0) {
949 /* remove note items that are no longer valid */
951 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
952 if (!(*i)->valid ()) {
954 i = _events.erase (i);
961 display_program_changes();
963 _marked_for_selection.clear ();
964 _marked_for_velocity.clear ();
966 /* we may have caused _events to contain things out of order (e.g. if a note
967 moved earlier or later). we don't generally need them in time order, but
968 make a note that a sort is required for those cases that require it.
975 MidiRegionView::display_program_changes()
977 boost::shared_ptr<Evoral::Control> control = _model->control(MidiPgmChangeAutomation);
982 Glib::Mutex::Lock lock (control->list()->lock());
984 uint8_t channel = control->parameter().channel();
986 for (AutomationList::const_iterator event = control->list()->begin();
987 event != control->list()->end(); ++event) {
988 double event_time = (*event)->when;
989 double program_number = floor((*event)->value + 0.5);
991 // Get current value of bank select MSB at time of the program change
992 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
993 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
995 if (msb_control != 0) {
996 msb = uint8_t(floor(msb_control->get_float(true, event_time) + 0.5));
999 // Get current value of bank select LSB at time of the program change
1000 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1001 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1003 if (lsb_control != 0) {
1004 lsb = uint8_t(floor(lsb_control->get_float(true, event_time) + 0.5));
1007 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
1009 boost::shared_ptr<MIDI::Name::Patch> patch =
1010 MIDI::Name::MidiPatchManager::instance().find_patch(
1011 _model_name, _custom_device_mode, channel, patch_key);
1013 PCEvent program_change(event_time, uint8_t(program_number), channel);
1016 add_pgm_change(program_change, patch->name());
1019 snprintf(buf, 4, "%d", int(program_number));
1020 add_pgm_change(program_change, buf);
1026 MidiRegionView::display_sysexes()
1028 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1029 Evoral::MusicalTime time = (*i)->time();
1034 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1035 str << int((*i)->buffer()[b]);
1036 if (b != (*i)->size() -1) {
1040 string text = str.str();
1042 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1044 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1046 double height = midi_stream_view()->contents_height();
1048 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1049 new CanvasSysEx(*this, *group, text, height, x, 1.0));
1051 // Show unless program change is beyond the region bounds
1052 if (time - _region->start() >= _region->length() || time < _region->start()) {
1058 _sys_exes.push_back(sysex);
1063 MidiRegionView::~MidiRegionView ()
1065 in_destructor = true;
1067 note_delete_connection.disconnect ();
1069 delete _list_editor;
1071 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1073 if (_active_notes) {
1080 delete _delta_command;
1084 MidiRegionView::region_resized (const PropertyChange& what_changed)
1086 RegionView::region_resized(what_changed);
1088 if (what_changed.contains (ARDOUR::Properties::position)) {
1089 set_duration(_region->length(), 0);
1090 if (_enable_display) {
1097 MidiRegionView::reset_width_dependent_items (double pixel_width)
1099 RegionView::reset_width_dependent_items(pixel_width);
1100 assert(_pixel_width == pixel_width);
1102 if (_enable_display) {
1108 MidiRegionView::set_height (double height)
1110 static const double FUDGE = 2.0;
1111 const double old_height = _height;
1112 RegionView::set_height(height);
1113 _height = height - FUDGE;
1115 apply_note_range(midi_stream_view()->lowest_note(),
1116 midi_stream_view()->highest_note(),
1117 height != old_height + FUDGE);
1120 name_pixbuf->raise_to_top();
1125 /** Apply the current note range from the stream view
1126 * by repositioning/hiding notes as necessary
1129 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1131 if (!_enable_display) {
1135 if (!force && _current_range_min == min && _current_range_max == max) {
1139 _current_range_min = min;
1140 _current_range_max = max;
1142 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1143 CanvasNoteEvent* event = *i;
1144 boost::shared_ptr<NoteType> note (event->note());
1146 if (note->note() < _current_range_min ||
1147 note->note() > _current_range_max) {
1153 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1155 const double y1 = midi_stream_view()->note_to_y(note->note());
1156 const double y2 = y1 + floor(midi_stream_view()->note_height());
1158 cnote->property_y1() = y1;
1159 cnote->property_y2() = y2;
1161 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1163 double x = trackview.editor().frame_to_pixel(
1164 beats_to_frames(note->time()) - _region->start());
1165 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1166 double y = midi_stream_view()->note_to_y(event->note()->note())
1167 + ((diamond_size-2.0) / 4.0);
1169 chit->set_height (diamond_size);
1170 chit->move (x - chit->x1(), y - chit->y1());
1177 MidiRegionView::add_ghost (TimeAxisView& tv)
1181 double unit_position = _region->position () / samples_per_unit;
1182 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1183 MidiGhostRegion* ghost;
1185 if (mtv && mtv->midi_view()) {
1186 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1187 to allow having midi notes on top of note lines and waveforms.
1189 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1191 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1194 ghost->set_height ();
1195 ghost->set_duration (_region->length() / samples_per_unit);
1196 ghosts.push_back (ghost);
1198 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1199 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1200 ghost->add_note(note);
1204 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1210 /** Begin tracking note state for successive calls to add_event
1213 MidiRegionView::begin_write()
1215 assert(!_active_notes);
1216 _active_notes = new CanvasNote*[128];
1217 for (unsigned i=0; i < 128; ++i) {
1218 _active_notes[i] = 0;
1223 /** Destroy note state for add_event
1226 MidiRegionView::end_write()
1228 delete[] _active_notes;
1230 _marked_for_selection.clear();
1231 _marked_for_velocity.clear();
1235 /** Resolve an active MIDI note (while recording).
1238 MidiRegionView::resolve_note(uint8_t note, double end_time)
1240 if (midi_view()->note_mode() != Sustained) {
1244 if (_active_notes && _active_notes[note]) {
1245 const nframes64_t end_time_frames = beats_to_frames(end_time);
1246 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1247 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1248 _active_notes[note] = 0;
1253 /** Extend active notes to rightmost edge of region (if length is changed)
1256 MidiRegionView::extend_active_notes()
1258 if (!_active_notes) {
1262 for (unsigned i=0; i < 128; ++i) {
1263 if (_active_notes[i]) {
1264 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1270 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1272 if (no_sound_notes || !trackview.editor().sound_notes()) {
1276 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1279 route_ui->midi_track()->write_immediate_event(
1280 note->on_event().size(), note->on_event().buffer());
1282 const double note_length_beats = (note->off_event().time() - note->on_event().time());
1283 nframes_t note_length_ms = beats_to_frames(note_length_beats)
1284 * (1000 / (double)route_ui->session()->nominal_frame_rate());
1285 Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(this, &MidiRegionView::play_midi_note_off), note),
1286 note_length_ms, G_PRIORITY_DEFAULT);
1290 MidiRegionView::play_midi_note_off(boost::shared_ptr<NoteType> note)
1292 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1295 route_ui->midi_track()->write_immediate_event(
1296 note->off_event().size(), note->off_event().buffer());
1302 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1304 const nframes64_t note_start_frames = beats_to_frames(note->time());
1306 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1307 (note_start_frames < _region->start());
1309 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1310 (note->note() <= midi_stream_view()->highest_note());
1316 MidiRegionView::update_note (CanvasNote* ev)
1318 boost::shared_ptr<NoteType> note = ev->note();
1321 const nframes64_t note_start_frames = beats_to_frames(note->time());
1322 const nframes64_t note_end_frames = beats_to_frames(note->end_time());
1324 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1325 const double y1 = midi_stream_view()->note_to_y(note->note());
1326 const double note_endpixel =
1327 trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1329 ev->property_x1() = x;
1330 ev->property_y1() = y1;
1331 if (note->length() > 0) {
1332 ev->property_x2() = note_endpixel;
1334 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1336 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1338 if (note->length() == 0) {
1339 if (_active_notes) {
1340 assert(note->note() < 128);
1341 // If this note is already active there's a stuck note,
1342 // finish the old note rectangle
1343 if (_active_notes[note->note()]) {
1344 CanvasNote* const old_rect = _active_notes[note->note()];
1345 boost::shared_ptr<NoteType> old_note = old_rect->note();
1346 old_rect->property_x2() = x;
1347 old_rect->property_outline_what() = (guint32) 0xF;
1349 _active_notes[note->note()] = ev;
1351 /* outline all but right edge */
1352 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1354 /* outline all edges */
1355 ev->property_outline_what() = (guint32) 0xF;
1360 MidiRegionView::update_hit (CanvasHit* ev)
1362 boost::shared_ptr<NoteType> note = ev->note();
1364 const nframes64_t note_start_frames = beats_to_frames(note->time());
1365 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1366 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1367 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1372 /** Add a MIDI note to the view (with length).
1374 * If in sustained mode, notes with length 0 will be considered active
1375 * notes, and resolve_note should be called when the corresponding note off
1376 * event arrives, to properly display the note.
1379 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1381 CanvasNoteEvent* event = 0;
1383 assert(note->time() >= 0);
1384 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1386 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1388 if (midi_view()->note_mode() == Sustained) {
1390 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1392 update_note (ev_rect);
1396 MidiGhostRegion* gr;
1398 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1399 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1400 gr->add_note(ev_rect);
1404 } else if (midi_view()->note_mode() == Percussive) {
1406 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1408 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1410 update_hit (ev_diamond);
1419 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1420 note_selected(event, true);
1423 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1424 event->show_velocity();
1426 event->on_channel_selection_change(_last_channel_selection);
1427 _events.push_back(event);
1438 MidiRegionView::add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1439 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1441 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1443 start_delta_command (_("step add"));
1444 delta_add_note (new_note, true, false);
1447 /* potentially extend region to hold new note */
1449 nframes64_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1450 nframes64_t region_end = _region->position() + _region->length() - 1;
1452 if (end_frame > region_end) {
1453 _region->set_length (end_frame, this);
1460 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1462 assert(program.time >= 0);
1464 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1465 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1467 double height = midi_stream_view()->contents_height();
1469 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1470 new CanvasProgramChange(*this, *group,
1475 _custom_device_mode,
1476 program.time, program.channel, program.value));
1478 // Show unless program change is beyond the region bounds
1479 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1485 _pgm_changes.push_back(pgm_change);
1489 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1491 cerr << "getting patch key at " << time << " for channel " << channel << endl;
1492 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1493 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1495 if (msb_control != 0) {
1496 msb = int(msb_control->get_float(true, time));
1497 cerr << "got msb " << msb;
1500 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1501 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1503 if (lsb_control != 0) {
1504 lsb = lsb_control->get_float(true, time);
1505 cerr << " got lsb " << lsb;
1508 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1509 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1510 float program_number = -1.0;
1511 if (program_control != 0) {
1512 program_number = program_control->get_float(true, time);
1513 cerr << " got program " << program_number << endl;
1516 key.msb = (int) floor(msb + 0.5);
1517 key.lsb = (int) floor(lsb + 0.5);
1518 key.program_number = (int) floor(program_number + 0.5);
1519 assert(key.is_sane());
1524 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1526 // TODO: Get the real event here and alter them at the original times
1527 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1528 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1529 if (msb_control != 0) {
1530 msb_control->set_float(float(new_patch.msb), true, old_program.time);
1533 // TODO: Get the real event here and alter them at the original times
1534 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1535 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1536 if (lsb_control != 0) {
1537 lsb_control->set_float(float(new_patch.lsb), true, old_program.time);
1540 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1541 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1543 assert(program_control != 0);
1544 program_control->set_float(float(new_patch.program_number), true, old_program.time);
1550 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1552 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1553 alter_program_change(program_change_event, new_patch);
1557 MidiRegionView::previous_program(CanvasProgramChange& program)
1559 MIDI::Name::PatchPrimaryKey key;
1560 get_patch_key_at(program.event_time(), program.channel(), key);
1562 boost::shared_ptr<MIDI::Name::Patch> patch =
1563 MIDI::Name::MidiPatchManager::instance().previous_patch(
1565 _custom_device_mode,
1569 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1571 alter_program_change(program_change_event, patch->patch_primary_key());
1576 MidiRegionView::next_program(CanvasProgramChange& program)
1578 MIDI::Name::PatchPrimaryKey key;
1579 get_patch_key_at(program.event_time(), program.channel(), key);
1581 boost::shared_ptr<MIDI::Name::Patch> patch =
1582 MIDI::Name::MidiPatchManager::instance().next_patch(
1584 _custom_device_mode,
1588 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1590 alter_program_change(program_change_event, patch->patch_primary_key());
1595 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1597 if (_selection.empty()) {
1601 if (_selection.erase (cne) > 0) {
1602 cerr << "Erased a CNE from selection\n";
1607 MidiRegionView::delete_selection()
1609 if (_selection.empty()) {
1613 start_delta_command (_("delete selection"));
1615 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1616 if ((*i)->selected()) {
1617 _delta_command->remove((*i)->note());
1627 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1629 start_delta_command (_("delete note"));
1630 _delta_command->remove (n);
1633 trackview.editor().hide_verbose_canvas_cursor ();
1637 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1639 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1640 if ((*i)->selected() && (*i) != ev) {
1641 (*i)->selected(false);
1642 (*i)->hide_velocity();
1650 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1652 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1655 Selection::iterator tmp = i;
1658 (*i)->selected (false);
1659 _selection.erase (i);
1668 /* don't bother with removing this regionview from the editor selection,
1669 since we're about to add another note, and thus put/keep this
1670 regionview in the editor selection.
1673 if (!ev->selected()) {
1674 add_to_selection (ev);
1679 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1681 uint8_t low_note = 127;
1682 uint8_t high_note = 0;
1683 MidiModel::Notes& notes (_model->notes());
1684 _optimization_iterator = _events.begin();
1690 if (extend && _selection.empty()) {
1696 /* scan existing selection to get note range */
1698 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1699 if ((*i)->note()->note() < low_note) {
1700 low_note = (*i)->note()->note();
1702 if ((*i)->note()->note() > high_note) {
1703 high_note = (*i)->note()->note();
1707 low_note = min (low_note, notenum);
1708 high_note = max (high_note, notenum);
1711 no_sound_notes = true;
1713 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1715 boost::shared_ptr<NoteType> note (*n);
1716 CanvasNoteEvent* cne;
1717 bool select = false;
1719 if (((0x0001 << note->channel()) & channel_mask) != 0) {
1721 if ((note->note() >= low_note && note->note() <= high_note)) {
1724 } else if (note->note() == notenum) {
1730 if ((cne = find_canvas_note (note)) != 0) {
1731 // extend is false because we've taken care of it,
1732 // since it extends by time range, not pitch.
1733 note_selected (cne, add, false);
1737 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1741 no_sound_notes = false;
1745 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1747 MidiModel::Notes& notes (_model->notes());
1748 _optimization_iterator = _events.begin();
1750 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1752 boost::shared_ptr<NoteType> note (*n);
1753 CanvasNoteEvent* cne;
1755 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1756 if ((cne = find_canvas_note (note)) != 0) {
1757 if (cne->selected()) {
1758 note_deselected (cne);
1760 note_selected (cne, true, false);
1768 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1771 clear_selection_except(ev);
1776 if (!ev->selected()) {
1777 add_to_selection (ev);
1781 /* find end of latest note selected, select all between that and the start of "ev" */
1783 Evoral::MusicalTime earliest = DBL_MAX;
1784 Evoral::MusicalTime latest = 0;
1786 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1787 if ((*i)->note()->end_time() > latest) {
1788 latest = (*i)->note()->end_time();
1790 if ((*i)->note()->time() < earliest) {
1791 earliest = (*i)->note()->time();
1795 if (ev->note()->end_time() > latest) {
1796 latest = ev->note()->end_time();
1799 if (ev->note()->time() < earliest) {
1800 earliest = ev->note()->time();
1803 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1805 /* find notes entirely within OR spanning the earliest..latest range */
1807 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1808 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1809 add_to_selection (*i);
1813 /* if events were guaranteed to be time sorted, we could do this.
1814 but as of sept 10th 2009, they no longer are.
1817 if ((*i)->note()->time() > latest) {
1826 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1828 remove_from_selection (ev);
1832 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1842 // TODO: Make this faster by storing the last updated selection rect, and only
1843 // adjusting things that are in the area that appears/disappeared.
1844 // We probably need a tree to be able to find events in O(log(n)) time.
1846 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1848 /* check if any corner of the note is inside the rect
1851 1) this is computing "touched by", not "contained by" the rect.
1852 2) this does not require that events be sorted in time.
1855 const double ix1 = (*i)->x1();
1856 const double ix2 = (*i)->x2();
1857 const double iy1 = (*i)->y1();
1858 const double iy2 = (*i)->y2();
1860 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1861 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1862 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1863 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1866 if (!(*i)->selected()) {
1867 add_to_selection (*i);
1869 } else if ((*i)->selected()) {
1870 // Not inside rectangle
1871 remove_from_selection (*i);
1877 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1879 Selection::iterator i = _selection.find (ev);
1881 if (i != _selection.end()) {
1882 _selection.erase (i);
1885 ev->selected (false);
1886 ev->hide_velocity ();
1888 if (_selection.empty()) {
1889 PublicEditor& editor (trackview.editor());
1890 editor.get_selection().remove (this);
1895 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
1897 bool add_mrv_selection = false;
1899 if (_selection.empty()) {
1900 add_mrv_selection = true;
1903 if (_selection.insert (ev).second) {
1904 ev->selected (true);
1905 play_midi_note ((ev)->note());
1908 if (add_mrv_selection) {
1909 PublicEditor& editor (trackview.editor());
1910 editor.get_selection().add (this);
1915 MidiRegionView::move_selection(double dx, double dy)
1917 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1918 (*i)->move_event(dx, dy);
1923 MidiRegionView::note_dropped(CanvasNoteEvent *, double dt, int8_t dnote)
1925 assert (!_selection.empty());
1927 uint8_t lowest_note_in_selection = 127;
1928 uint8_t highest_note_in_selection = 0;
1929 uint8_t highest_note_difference = 0;
1931 // find highest and lowest notes first
1933 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1934 uint8_t pitch = (*i)->note()->note();
1935 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
1936 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
1940 cerr << "dnote: " << (int) dnote << endl;
1941 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
1942 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
1943 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
1944 << int(highest_note_in_selection) << endl;
1945 cerr << "selection size: " << _selection.size() << endl;
1946 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
1949 // Make sure the note pitch does not exceed the MIDI standard range
1950 if (highest_note_in_selection + dnote > 127) {
1951 highest_note_difference = highest_note_in_selection - 127;
1954 start_diff_command(_("move notes"));
1956 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
1958 nframes64_t start_frames = beats_to_frames((*i)->note()->time());
1961 start_frames += snap_frame_to_frame(trackview.editor().pixel_to_frame(dt));
1963 start_frames -= snap_frame_to_frame(trackview.editor().pixel_to_frame(-dt));
1966 Evoral::MusicalTime new_time = frames_to_beats(start_frames);
1972 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
1974 uint8_t original_pitch = (*i)->note()->note();
1975 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
1977 // keep notes in standard midi range
1978 clamp_to_0_127(new_pitch);
1980 // keep original pitch if note is dragged outside valid midi range
1981 if ((original_pitch != 0 && new_pitch == 0)
1982 || (original_pitch != 127 && new_pitch == 127)) {
1983 new_pitch = original_pitch;
1986 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
1987 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
1989 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
1994 // care about notes being moved beyond the upper/lower bounds on the canvas
1995 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
1996 highest_note_in_selection > midi_stream_view()->highest_note()) {
1997 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2002 MidiRegionView::snap_pixel_to_frame(double x)
2004 PublicEditor& editor = trackview.editor();
2005 // x is region relative, convert it to global absolute frames
2006 nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
2007 editor.snap_to(frame);
2008 return frame - _region->position(); // convert back to region relative
2012 MidiRegionView::snap_frame_to_frame(nframes64_t x)
2014 PublicEditor& editor = trackview.editor();
2015 // x is region relative, convert it to global absolute frames
2016 nframes64_t frame = x + _region->position();
2017 editor.snap_to(frame);
2018 return frame - _region->position(); // convert back to region relative
2022 MidiRegionView::snap_to_pixel(double x)
2024 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2028 MidiRegionView::get_position_pixels()
2030 nframes64_t region_frame = get_position();
2031 return trackview.editor().frame_to_pixel(region_frame);
2035 MidiRegionView::get_end_position_pixels()
2037 nframes64_t frame = get_position() + get_duration ();
2038 return trackview.editor().frame_to_pixel(frame);
2042 MidiRegionView::beats_to_frames(double beats) const
2044 return _time_converter.to(beats);
2048 MidiRegionView::frames_to_beats(nframes64_t frames) const
2050 return _time_converter.from(frames);
2054 MidiRegionView::begin_resizing (bool /*at_front*/)
2056 _resize_data.clear();
2058 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2059 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2061 // only insert CanvasNotes into the map
2063 NoteResizeData *resize_data = new NoteResizeData();
2064 resize_data->canvas_note = note;
2066 // create a new SimpleRect from the note which will be the resize preview
2067 SimpleRect *resize_rect = new SimpleRect(
2068 *group, note->x1(), note->y1(), note->x2(), note->y2());
2070 // calculate the colors: get the color settings
2071 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2072 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2075 // make the resize preview notes more transparent and bright
2076 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2078 // calculate color based on note velocity
2079 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2080 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity()),
2084 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2085 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2087 resize_data->resize_rect = resize_rect;
2088 _resize_data.push_back(resize_data);
2094 MidiRegionView::update_resizing (bool at_front, double delta_x, bool relative)
2096 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2097 SimpleRect* resize_rect = (*i)->resize_rect;
2098 CanvasNote* canvas_note = (*i)->canvas_note;
2103 current_x = canvas_note->x1() + delta_x;
2105 // x is in track relative, transform it to region relative
2106 current_x = delta_x - get_position_pixels();
2110 current_x = canvas_note->x2() + delta_x;
2112 // x is in track relative, transform it to region relative
2113 current_x = delta_x - get_end_position_pixels ();
2118 resize_rect->property_x1() = snap_to_pixel(current_x);
2119 resize_rect->property_x2() = canvas_note->x2();
2121 resize_rect->property_x2() = snap_to_pixel(current_x);
2122 resize_rect->property_x1() = canvas_note->x1();
2128 MidiRegionView::commit_resizing (bool at_front, double delta_x, bool relative)
2130 start_diff_command(_("resize notes"));
2132 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2133 CanvasNote* canvas_note = (*i)->canvas_note;
2134 SimpleRect* resize_rect = (*i)->resize_rect;
2135 const double region_start = get_position_pixels();
2140 current_x = canvas_note->x1() + delta_x;
2142 // x is in track relative, transform it to region relative
2143 current_x = region_start + delta_x;
2147 current_x = canvas_note->x2() + delta_x;
2149 // x is in track relative, transform it to region relative
2150 current_x = region_start + delta_x;
2154 current_x = snap_pixel_to_frame (current_x);
2155 current_x = frames_to_beats (current_x);
2157 if (at_front && current_x < canvas_note->note()->end_time()) {
2158 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2160 double len = canvas_note->note()->time() - current_x;
2161 len += canvas_note->note()->length();
2164 /* XXX convert to beats */
2165 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2170 double len = current_x - canvas_note->note()->time();
2173 /* XXX convert to beats */
2174 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2182 _resize_data.clear();
2187 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2189 uint8_t new_velocity;
2192 new_velocity = event->note()->velocity() + velocity;
2193 clamp_to_0_127(new_velocity);
2195 new_velocity = velocity;
2198 event->show_velocity ();
2200 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2204 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2209 new_note = event->note()->note() + note;
2214 clamp_to_0_127 (new_note);
2215 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2219 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2221 bool change_start = false;
2222 bool change_length = false;
2223 Evoral::MusicalTime new_start;
2224 Evoral::MusicalTime new_length;
2226 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2228 front_delta: if positive - move the start of the note later in time (shortening it)
2229 if negative - move the start of the note earlier in time (lengthening it)
2231 end_delta: if positive - move the end of the note later in time (lengthening it)
2232 if negative - move the end of the note earlier in time (shortening it)
2236 if (front_delta < 0) {
2238 if (event->note()->time() < -front_delta) {
2241 new_start = event->note()->time() + front_delta; // moves earlier
2244 /* start moved toward zero, so move the end point out to where it used to be.
2245 Note that front_delta is negative, so this increases the length.
2248 new_length = event->note()->length() - front_delta;
2249 change_start = true;
2250 change_length = true;
2254 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2256 if (new_pos < event->note()->end_time()) {
2257 new_start = event->note()->time() + front_delta;
2258 /* start moved toward the end, so move the end point back to where it used to be */
2259 new_length = event->note()->length() - front_delta;
2260 change_start = true;
2261 change_length = true;
2268 bool can_change = true;
2269 if (end_delta < 0) {
2270 if (event->note()->length() < -end_delta) {
2276 new_length = event->note()->length() + end_delta;
2277 change_length = true;
2282 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2285 if (change_length) {
2286 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2291 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2293 Evoral::MusicalTime new_time;
2297 if (event->note()->time() < -delta) {
2300 new_time = event->note()->time() + delta;
2303 new_time = event->note()->time() + delta;
2309 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2313 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2317 if (_selection.empty()) {
2332 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2333 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2339 start_diff_command(_("change velocities"));
2341 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2342 Selection::iterator next = i;
2344 change_note_velocity (*i, delta, true);
2353 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2355 if (_selection.empty()) {
2372 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2374 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2378 if ((int8_t) (*i)->note()->note() + delta > 127) {
2385 start_diff_command (_("transpose"));
2387 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2388 Selection::iterator next = i;
2390 change_note_note (*i, delta, true);
2398 MidiRegionView::change_note_lengths (bool fine, bool shorter, bool start, bool end)
2400 Evoral::MusicalTime delta;
2405 /* grab the current grid distance */
2407 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2409 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2410 cerr << "Grid type not available as beats - TO BE FIXED\n";
2419 start_diff_command (_("change note lengths"));
2421 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2422 Selection::iterator next = i;
2425 /* note the negation of the delta for start */
2427 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2436 MidiRegionView::nudge_notes (bool forward)
2438 if (_selection.empty()) {
2442 /* pick a note as the point along the timeline to get the nudge distance.
2443 its not necessarily the earliest note, so we may want to pull the notes out
2444 into a vector and sort before using the first one.
2447 nframes64_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2449 nframes64_t distance;
2451 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2453 /* grid is off - use nudge distance */
2455 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2461 nframes64_t next_pos = ref_point;
2464 /* XXX need check on max_frames, but that needs max_frames64 or something */
2467 if (next_pos == 0) {
2473 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2474 distance = ref_point - next_pos;
2477 if (distance == 0) {
2481 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2487 start_diff_command (_("nudge"));
2489 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2490 Selection::iterator next = i;
2492 change_note_time (*i, delta, true);
2500 MidiRegionView::change_channel(uint8_t channel)
2502 start_diff_command(_("change channel"));
2503 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2504 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2512 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2514 if (_mouse_state == SelectTouchDragging) {
2515 note_selected (ev, true);
2518 show_verbose_canvas_cursor (ev->note ());
2522 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent* note)
2524 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2525 (*i)->hide_velocity ();
2528 trackview.editor().hide_verbose_canvas_cursor ();
2532 MidiRegionView::switch_source(boost::shared_ptr<Source> src)
2534 boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(src);
2536 display_model(msrc->model());
2540 MidiRegionView::set_frame_color()
2543 if (_selected && should_show_selection) {
2544 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2546 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2552 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2556 case FilterChannels:
2557 _force_channel = -1;
2560 _force_channel = mask;
2561 mask = 0xFFFF; // Show all notes as active (below)
2564 // Update notes for selection
2565 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2566 (*i)->on_channel_selection_change(mask);
2569 _last_channel_selection = mask;
2573 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2575 _model_name = model;
2576 _custom_device_mode = custom_device_mode;
2581 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2583 if (_selection.empty()) {
2587 PublicEditor& editor (trackview.editor());
2592 editor.get_cut_buffer().add (selection_as_cut_buffer());
2600 start_delta_command();
2602 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2608 delta_remove_note (*i);
2618 MidiRegionView::selection_as_cut_buffer () const
2622 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2623 NoteType* n = (*i)->note().get();
2624 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2627 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2634 MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
2640 start_delta_command (_("paste"));
2642 Evoral::MusicalTime beat_delta;
2643 Evoral::MusicalTime paste_pos_beats;
2644 Evoral::MusicalTime duration;
2645 Evoral::MusicalTime end_point;
2647 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2648 paste_pos_beats = frames_to_beats (pos - _region->position());
2649 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2650 paste_pos_beats = 0;
2652 _selection.clear ();
2654 for (int n = 0; n < (int) times; ++n) {
2656 cerr << "Pasting " << mcb.notes().size() << " for the " << n+1 << "th time\n";
2658 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2660 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2661 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2663 /* make all newly added notes selected */
2665 delta_add_note (copied_note, true);
2666 end_point = copied_note->end_time();
2669 paste_pos_beats += duration;
2672 /* if we pasted past the current end of the region, extend the region */
2674 nframes64_t end_frame = _region->position() + beats_to_frames (end_point);
2675 nframes64_t region_end = _region->position() + _region->length() - 1;
2677 if (end_frame > region_end) {
2679 cerr << "region end is now " << end_frame << " to extend from " << region_end << endl;
2681 trackview.session()->begin_reversible_command (_("paste"));
2683 _region->clear_history ();
2684 _region->set_length (end_frame, this);
2685 trackview.session()->add_command (new StatefulDiffCommand (_region));
2688 cerr << "region end finally at " << _region->position() + _region->length() - 1;
2692 struct EventNoteTimeEarlyFirstComparator {
2693 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2694 return a->note()->time() < b->note()->time();
2699 MidiRegionView::time_sort_events ()
2701 if (!_sort_needed) {
2705 EventNoteTimeEarlyFirstComparator cmp;
2708 _sort_needed = false;
2712 MidiRegionView::goto_next_note ()
2714 // nframes64_t pos = -1;
2715 bool use_next = false;
2717 if (_events.back()->selected()) {
2721 time_sort_events ();
2723 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2724 if ((*i)->selected()) {
2727 } else if (use_next) {
2729 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2734 /* use the first one */
2736 unique_select (_events.front());
2741 MidiRegionView::goto_previous_note ()
2743 // nframes64_t pos = -1;
2744 bool use_next = false;
2746 if (_events.front()->selected()) {
2750 time_sort_events ();
2752 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2753 if ((*i)->selected()) {
2756 } else if (use_next) {
2758 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2763 /* use the last one */
2765 unique_select (*(_events.rbegin()));
2769 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2771 bool had_selected = false;
2773 time_sort_events ();
2775 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2776 if ((*i)->selected()) {
2777 selected.insert ((*i)->note());
2778 had_selected = true;
2782 if (allow_all_if_none_selected && !had_selected) {
2783 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2784 selected.insert ((*i)->note());
2790 MidiRegionView::update_ghost_note (double x, double y)
2796 nframes64_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
2797 trackview.editor().snap_to (f);
2798 f -= _region->position ();
2801 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
2806 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
2808 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
2809 _ghost_note->note()->set_length (length);
2810 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
2812 update_note (_ghost_note);
2814 show_verbose_canvas_cursor (_ghost_note->note ());
2818 MidiRegionView::create_ghost_note (double x, double y)
2823 boost::shared_ptr<NoteType> g (new NoteType);
2824 _ghost_note = new NoEventCanvasNote (*this, *group, g);
2825 update_ghost_note (x, y);
2826 _ghost_note->show ();
2831 show_verbose_canvas_cursor (_ghost_note->note ());
2835 MidiRegionView::snap_changed ()
2841 create_ghost_note (_last_ghost_x, _last_ghost_y);
2845 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
2848 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (n->note()).c_str(), (int) n->note ());
2849 trackview.editor().show_verbose_canvas_cursor_with (buf);