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 */
280 Keyboard::magic_widget_grab_focus();
283 if (trackview.editor().current_mouse_mode() == MouseRange) {
284 create_ghost_note (ev->x, ev->y);
291 MidiRegionView::leave_notify (GdkEventCrossing* ev)
293 trackview.editor().hide_verbose_canvas_cursor ();
300 MidiRegionView::button_press (GdkEventButton* ev)
304 group->w2i (_last_x, _last_y);
306 if (_mouse_state != SelectTouchDragging && ev->button == 1) {
307 _pressed_button = ev->button;
308 _mouse_state = Pressed;
311 _pressed_button = ev->button;
317 MidiRegionView::button_release (GdkEventButton* ev)
319 double event_x, event_y;
320 nframes64_t event_frame = 0;
324 group->w2i(event_x, event_y);
325 group->ungrab(ev->time);
326 event_frame = trackview.editor().pixel_to_frame(event_x);
328 if (ev->button == 3) {
330 } else if (_pressed_button != 1) {
334 switch (_mouse_state) {
335 case Pressed: // Clicked
336 switch (trackview.editor().current_mouse_mode()) {
344 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
349 create_note_at (event_x, event_y, beats);
357 case SelectRectDragging: // Select drag done
363 case AddDragging: // Add drag done
365 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
366 const double x = _drag_rect->property_x1();
367 const double length = trackview.editor().pixel_to_frame
368 (_drag_rect->property_x2() - _drag_rect->property_x1());
370 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length));
376 create_ghost_note (ev->x, ev->y);
386 MidiRegionView::motion (GdkEventMotion* ev)
388 double event_x, event_y;
389 nframes64_t event_frame = 0;
393 group->w2i(event_x, event_y);
395 // convert event_x to global frame
396 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
397 trackview.editor().snap_to(event_frame);
398 // convert event_frame back to local coordinates relative to position
399 event_frame -= _region->position();
402 update_ghost_note (ev->x, ev->y);
405 switch (_mouse_state) {
406 case Pressed: // Maybe start a drag, if we've moved a bit
408 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
409 /* no appreciable movement since the button was pressed */
414 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
415 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
416 Gdk::Cursor(Gdk::FLEUR), ev->time);
419 _drag_start_x = event_x;
420 _drag_start_y = event_y;
422 _drag_rect = new ArdourCanvas::SimpleRect(*group);
423 _drag_rect->property_x1() = event_x;
424 _drag_rect->property_y1() = event_y;
425 _drag_rect->property_x2() = event_x;
426 _drag_rect->property_y2() = event_y;
427 _drag_rect->property_outline_what() = 0xFF;
428 _drag_rect->property_outline_color_rgba()
429 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
430 _drag_rect->property_fill_color_rgba()
431 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
433 _mouse_state = SelectRectDragging;
436 // Add note drag start
437 } else if (trackview.editor().internal_editing()) {
442 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
443 Gdk::Cursor(Gdk::FLEUR), ev->time);
446 _drag_start_x = event_x;
447 _drag_start_y = event_y;
449 _drag_rect = new ArdourCanvas::SimpleRect(*group);
450 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
452 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
453 midi_stream_view()->y_to_note(event_y));
454 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
455 _drag_rect->property_y2() = _drag_rect->property_y1()
456 + floor(midi_stream_view()->note_height());
457 _drag_rect->property_outline_what() = 0xFF;
458 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
459 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
461 _mouse_state = AddDragging;
467 case SelectRectDragging: // Select drag motion
468 case AddDragging: // Add note drag motion
472 GdkModifierType state;
473 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
478 if (_mouse_state == AddDragging)
479 event_x = trackview.editor().frame_to_pixel(event_frame);
482 if (event_x > _drag_start_x)
483 _drag_rect->property_x2() = event_x;
485 _drag_rect->property_x1() = event_x;
488 if (_drag_rect && _mouse_state == SelectRectDragging) {
489 if (event_y > _drag_start_y)
490 _drag_rect->property_y2() = event_y;
492 _drag_rect->property_y1() = event_y;
494 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
500 case SelectTouchDragging:
512 MidiRegionView::scroll (GdkEventScroll* ev)
514 bool fine = Keyboard::modifier_state_equals (ev->state, Keyboard::Level4Modifier);
516 if (ev->direction == GDK_SCROLL_UP) {
517 change_velocities (true, fine, false);
519 } else if (ev->direction == GDK_SCROLL_DOWN) {
520 change_velocities (false, fine, false);
527 MidiRegionView::key_press (GdkEventKey* ev)
529 /* since GTK bindings are generally activated on press, and since
530 detectable auto-repeat is the name of the game and only sends
531 repeated presses, carry out key actions at key press, not release.
534 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
535 _mouse_state = SelectTouchDragging;
538 } else if (ev->keyval == GDK_Escape) {
542 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
544 bool start = (ev->keyval == GDK_comma);
545 bool end = (ev->keyval == GDK_period);
546 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
547 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
549 change_note_lengths (fine, shorter, start, end);
553 } else if (ev->keyval == GDK_Delete) {
558 } else if (ev->keyval == GDK_Tab) {
560 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
561 goto_previous_note ();
567 } else if (ev->keyval == GDK_Up) {
569 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
570 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
572 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
573 change_velocities (true, fine, allow_smush);
575 transpose (true, fine, allow_smush);
579 } else if (ev->keyval == GDK_Down) {
581 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
582 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
584 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
585 change_velocities (false, fine, allow_smush);
587 transpose (false, fine, allow_smush);
591 } else if (ev->keyval == GDK_Left) {
596 } else if (ev->keyval == GDK_Right) {
601 } else if (ev->keyval == GDK_Control_L) {
604 } else if (ev->keyval == GDK_r) {
605 /* if we're not step editing, this really doesn't matter */
606 midi_view()->step_edit_rest ();
614 MidiRegionView::key_release (GdkEventKey* ev)
616 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
624 MidiRegionView::show_list_editor ()
627 _list_editor = new MidiListEditor (trackview.session(), midi_region());
629 _list_editor->present ();
632 /** Add a note to the model, and the view, at a canvas (click) coordinate.
633 * \param x horizontal position in pixels
634 * \param y vertical position in pixels
635 * \param length duration of the note in beats */
637 MidiRegionView::create_note_at(double x, double y, double length)
639 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
640 MidiStreamView* const view = mtv->midi_view();
642 double note = midi_stream_view()->y_to_note(y);
645 assert(note <= 127.0);
647 // Start of note in frames relative to region start
648 nframes64_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
649 assert(start_frames >= 0);
652 length = frames_to_beats(
653 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
655 assert (length != 0);
657 const boost::shared_ptr<NoteType> new_note(new NoteType(0,
658 frames_to_beats(start_frames + _region->start()), length,
659 (uint8_t)note, 0x40));
661 if (_model->contains (new_note)) {
665 view->update_note_range(new_note->note());
667 MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
669 _model->apply_command(*trackview.session(), cmd);
671 play_midi_note (new_note);
675 MidiRegionView::clear_events()
680 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
681 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
686 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
691 _pgm_changes.clear();
693 _optimization_iterator = _events.end();
698 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
701 content_connection.disconnect ();
702 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
706 if (_enable_display) {
713 MidiRegionView::start_delta_command(string name)
715 if (!_delta_command) {
716 _delta_command = _model->new_delta_command(name);
721 MidiRegionView::start_diff_command(string name)
723 if (!_diff_command) {
724 _diff_command = _model->new_diff_command(name);
729 MidiRegionView::delta_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
731 if (_delta_command) {
732 _delta_command->add(note);
735 _marked_for_selection.insert(note);
738 _marked_for_velocity.insert(note);
743 MidiRegionView::delta_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
745 if (_delta_command && ev->note()) {
746 _delta_command->remove(ev->note());
751 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
752 MidiModel::DiffCommand::Property property,
756 _diff_command->change (ev->note(), property, val);
761 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
762 MidiModel::DiffCommand::Property property,
763 Evoral::MusicalTime val)
766 _diff_command->change (ev->note(), property, val);
771 MidiRegionView::apply_delta()
773 if (!_delta_command) {
777 // Mark all selected notes for selection when model reloads
778 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
779 _marked_for_selection.insert((*i)->note());
782 _model->apply_command(*trackview.session(), _delta_command);
784 midi_view()->midi_track()->playlist_modified();
786 _marked_for_selection.clear();
787 _marked_for_velocity.clear();
791 MidiRegionView::apply_diff ()
793 if (!_diff_command) {
797 _model->apply_command(*trackview.session(), _diff_command);
799 midi_view()->midi_track()->playlist_modified();
801 _marked_for_velocity.clear();
805 MidiRegionView::apply_delta_as_subcommand()
807 if (!_delta_command) {
811 // Mark all selected notes for selection when model reloads
812 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
813 _marked_for_selection.insert((*i)->note());
816 _model->apply_command_as_subcommand(*trackview.session(), _delta_command);
818 midi_view()->midi_track()->playlist_modified();
820 _marked_for_selection.clear();
821 _marked_for_velocity.clear();
825 MidiRegionView::apply_diff_as_subcommand()
827 if (!_diff_command) {
831 // Mark all selected notes for selection when model reloads
832 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
833 _marked_for_selection.insert((*i)->note());
836 _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
838 midi_view()->midi_track()->playlist_modified();
840 _marked_for_selection.clear();
841 _marked_for_velocity.clear();
845 MidiRegionView::abort_command()
847 delete _delta_command;
849 delete _diff_command;
855 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
857 if (_optimization_iterator != _events.end()) {
858 ++_optimization_iterator;
861 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
862 return *_optimization_iterator;
865 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
866 if ((*_optimization_iterator)->note() == note) {
867 return *_optimization_iterator;
875 MidiRegionView::redisplay_model()
877 // Don't redisplay the model if we're currently recording and displaying that
883 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
887 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
891 MidiModel::ReadLock lock(_model->read_lock());
893 MidiModel::Notes& notes (_model->notes());
894 _optimization_iterator = _events.begin();
896 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
898 boost::shared_ptr<NoteType> note (*n);
899 CanvasNoteEvent* cne;
902 if (note_in_region_range (note, visible)) {
904 if ((cne = find_canvas_note (note)) != 0) {
911 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
913 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
925 add_note (note, visible);
930 if ((cne = find_canvas_note (note)) != 0) {
938 /* remove note items that are no longer valid */
940 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
941 if (!(*i)->valid ()) {
943 i = _events.erase (i);
950 display_program_changes();
952 _marked_for_selection.clear ();
953 _marked_for_velocity.clear ();
955 /* we may have caused _events to contain things out of order (e.g. if a note
956 moved earlier or later). we don't generally need them in time order, but
957 make a note that a sort is required for those cases that require it.
964 MidiRegionView::display_program_changes()
966 boost::shared_ptr<Evoral::Control> control = _model->control(MidiPgmChangeAutomation);
971 Glib::Mutex::Lock lock (control->list()->lock());
973 uint8_t channel = control->parameter().channel();
975 for (AutomationList::const_iterator event = control->list()->begin();
976 event != control->list()->end(); ++event) {
977 double event_time = (*event)->when;
978 double program_number = floor((*event)->value + 0.5);
980 // Get current value of bank select MSB at time of the program change
981 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
982 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
984 if (msb_control != 0) {
985 msb = uint8_t(floor(msb_control->get_float(true, event_time) + 0.5));
988 // Get current value of bank select LSB at time of the program change
989 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
990 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
992 if (lsb_control != 0) {
993 lsb = uint8_t(floor(lsb_control->get_float(true, event_time) + 0.5));
996 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
998 boost::shared_ptr<MIDI::Name::Patch> patch =
999 MIDI::Name::MidiPatchManager::instance().find_patch(
1000 _model_name, _custom_device_mode, channel, patch_key);
1002 PCEvent program_change(event_time, uint8_t(program_number), channel);
1005 add_pgm_change(program_change, patch->name());
1008 snprintf(buf, 4, "%d", int(program_number));
1009 add_pgm_change(program_change, buf);
1015 MidiRegionView::display_sysexes()
1017 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1018 Evoral::MusicalTime time = (*i)->time();
1023 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1024 str << int((*i)->buffer()[b]);
1025 if (b != (*i)->size() -1) {
1029 string text = str.str();
1031 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1033 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1035 double height = midi_stream_view()->contents_height();
1037 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1038 new CanvasSysEx(*this, *group, text, height, x, 1.0));
1040 // Show unless program change is beyond the region bounds
1041 if (time - _region->start() >= _region->length() || time < _region->start()) {
1047 _sys_exes.push_back(sysex);
1052 MidiRegionView::~MidiRegionView ()
1054 in_destructor = true;
1056 note_delete_connection.disconnect ();
1058 delete _list_editor;
1060 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1062 if (_active_notes) {
1069 delete _delta_command;
1073 MidiRegionView::region_resized (const PropertyChange& what_changed)
1075 RegionView::region_resized(what_changed);
1077 if (what_changed.contains (ARDOUR::Properties::position)) {
1078 set_duration(_region->length(), 0);
1079 if (_enable_display) {
1086 MidiRegionView::reset_width_dependent_items (double pixel_width)
1088 RegionView::reset_width_dependent_items(pixel_width);
1089 assert(_pixel_width == pixel_width);
1091 if (_enable_display) {
1097 MidiRegionView::set_height (double height)
1099 static const double FUDGE = 2.0;
1100 const double old_height = _height;
1101 RegionView::set_height(height);
1102 _height = height - FUDGE;
1104 apply_note_range(midi_stream_view()->lowest_note(),
1105 midi_stream_view()->highest_note(),
1106 height != old_height + FUDGE);
1109 name_pixbuf->raise_to_top();
1114 /** Apply the current note range from the stream view
1115 * by repositioning/hiding notes as necessary
1118 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1120 if (!_enable_display) {
1124 if (!force && _current_range_min == min && _current_range_max == max) {
1128 _current_range_min = min;
1129 _current_range_max = max;
1131 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1132 CanvasNoteEvent* event = *i;
1133 boost::shared_ptr<NoteType> note (event->note());
1135 if (note->note() < _current_range_min ||
1136 note->note() > _current_range_max) {
1142 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1144 const double y1 = midi_stream_view()->note_to_y(note->note());
1145 const double y2 = y1 + floor(midi_stream_view()->note_height());
1147 cnote->property_y1() = y1;
1148 cnote->property_y2() = y2;
1150 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1152 double x = trackview.editor().frame_to_pixel(
1153 beats_to_frames(note->time()) - _region->start());
1154 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1155 double y = midi_stream_view()->note_to_y(event->note()->note())
1156 + ((diamond_size-2.0) / 4.0);
1158 chit->set_height (diamond_size);
1159 chit->move (x - chit->x1(), y - chit->y1());
1166 MidiRegionView::add_ghost (TimeAxisView& tv)
1170 double unit_position = _region->position () / samples_per_unit;
1171 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1172 MidiGhostRegion* ghost;
1174 if (mtv && mtv->midi_view()) {
1175 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1176 to allow having midi notes on top of note lines and waveforms.
1178 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1180 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1183 ghost->set_height ();
1184 ghost->set_duration (_region->length() / samples_per_unit);
1185 ghosts.push_back (ghost);
1187 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1188 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1189 ghost->add_note(note);
1193 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1199 /** Begin tracking note state for successive calls to add_event
1202 MidiRegionView::begin_write()
1204 assert(!_active_notes);
1205 _active_notes = new CanvasNote*[128];
1206 for (unsigned i=0; i < 128; ++i) {
1207 _active_notes[i] = 0;
1212 /** Destroy note state for add_event
1215 MidiRegionView::end_write()
1217 delete[] _active_notes;
1219 _marked_for_selection.clear();
1220 _marked_for_velocity.clear();
1224 /** Resolve an active MIDI note (while recording).
1227 MidiRegionView::resolve_note(uint8_t note, double end_time)
1229 if (midi_view()->note_mode() != Sustained) {
1233 if (_active_notes && _active_notes[note]) {
1234 const nframes64_t end_time_frames = beats_to_frames(end_time);
1235 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1236 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1237 _active_notes[note] = 0;
1242 /** Extend active notes to rightmost edge of region (if length is changed)
1245 MidiRegionView::extend_active_notes()
1247 if (!_active_notes) {
1251 for (unsigned i=0; i < 128; ++i) {
1252 if (_active_notes[i]) {
1253 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1259 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1261 if (no_sound_notes || !trackview.editor().sound_notes()) {
1265 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1268 route_ui->midi_track()->write_immediate_event(
1269 note->on_event().size(), note->on_event().buffer());
1271 const double note_length_beats = (note->off_event().time() - note->on_event().time());
1272 nframes_t note_length_ms = beats_to_frames(note_length_beats)
1273 * (1000 / (double)route_ui->session()->nominal_frame_rate());
1274 Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(this, &MidiRegionView::play_midi_note_off), note),
1275 note_length_ms, G_PRIORITY_DEFAULT);
1279 MidiRegionView::play_midi_note_off(boost::shared_ptr<NoteType> note)
1281 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1284 route_ui->midi_track()->write_immediate_event(
1285 note->off_event().size(), note->off_event().buffer());
1291 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1293 const nframes64_t note_start_frames = beats_to_frames(note->time());
1295 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1296 (note_start_frames < _region->start());
1298 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1299 (note->note() <= midi_stream_view()->highest_note());
1305 MidiRegionView::update_note (CanvasNote* ev)
1307 boost::shared_ptr<NoteType> note = ev->note();
1309 const nframes64_t note_start_frames = beats_to_frames(note->time());
1310 const nframes64_t note_end_frames = beats_to_frames(note->end_time());
1312 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1313 const double y1 = midi_stream_view()->note_to_y(note->note());
1314 const double note_endpixel =
1315 trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1317 ev->property_x1() = x;
1318 ev->property_y1() = y1;
1319 if (note->length() > 0) {
1320 ev->property_x2() = note_endpixel;
1322 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1324 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1326 if (note->length() == 0) {
1327 if (_active_notes) {
1328 assert(note->note() < 128);
1329 // If this note is already active there's a stuck note,
1330 // finish the old note rectangle
1331 if (_active_notes[note->note()]) {
1332 CanvasNote* const old_rect = _active_notes[note->note()];
1333 boost::shared_ptr<NoteType> old_note = old_rect->note();
1334 old_rect->property_x2() = x;
1335 old_rect->property_outline_what() = (guint32) 0xF;
1337 _active_notes[note->note()] = ev;
1339 /* outline all but right edge */
1340 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1342 /* outline all edges */
1343 ev->property_outline_what() = (guint32) 0xF;
1348 MidiRegionView::update_hit (CanvasHit* ev)
1350 boost::shared_ptr<NoteType> note = ev->note();
1352 const nframes64_t note_start_frames = beats_to_frames(note->time());
1353 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1354 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1355 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1360 /** Add a MIDI note to the view (with length).
1362 * If in sustained mode, notes with length 0 will be considered active
1363 * notes, and resolve_note should be called when the corresponding note off
1364 * event arrives, to properly display the note.
1367 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1369 CanvasNoteEvent* event = 0;
1371 assert(note->time() >= 0);
1372 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1374 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1376 if (midi_view()->note_mode() == Sustained) {
1378 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1380 update_note (ev_rect);
1384 MidiGhostRegion* gr;
1386 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1387 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1388 gr->add_note(ev_rect);
1392 } else if (midi_view()->note_mode() == Percussive) {
1394 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1396 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1398 update_hit (ev_diamond);
1407 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1408 note_selected(event, true);
1411 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1412 event->show_velocity();
1414 event->on_channel_selection_change(_last_channel_selection);
1415 _events.push_back(event);
1426 MidiRegionView::add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1427 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1429 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1431 start_delta_command (_("step add"));
1432 delta_add_note (new_note, true, false);
1435 /* potentially extend region to hold new note */
1437 nframes64_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1438 nframes64_t region_end = _region->position() + _region->length() - 1;
1440 if (end_frame > region_end) {
1441 _region->set_length (end_frame, this);
1448 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1450 assert(program.time >= 0);
1452 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1453 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1455 double height = midi_stream_view()->contents_height();
1457 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1458 new CanvasProgramChange(*this, *group,
1463 _custom_device_mode,
1464 program.time, program.channel, program.value));
1466 // Show unless program change is beyond the region bounds
1467 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1473 _pgm_changes.push_back(pgm_change);
1477 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1479 cerr << "getting patch key at " << time << " for channel " << channel << endl;
1480 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1481 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1483 if (msb_control != 0) {
1484 msb = int(msb_control->get_float(true, time));
1485 cerr << "got msb " << msb;
1488 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1489 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1491 if (lsb_control != 0) {
1492 lsb = lsb_control->get_float(true, time);
1493 cerr << " got lsb " << lsb;
1496 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1497 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1498 float program_number = -1.0;
1499 if (program_control != 0) {
1500 program_number = program_control->get_float(true, time);
1501 cerr << " got program " << program_number << endl;
1504 key.msb = (int) floor(msb + 0.5);
1505 key.lsb = (int) floor(lsb + 0.5);
1506 key.program_number = (int) floor(program_number + 0.5);
1507 assert(key.is_sane());
1512 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1514 // TODO: Get the real event here and alter them at the original times
1515 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1516 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1517 if (msb_control != 0) {
1518 msb_control->set_float(float(new_patch.msb), true, old_program.time);
1521 // TODO: Get the real event here and alter them at the original times
1522 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1523 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1524 if (lsb_control != 0) {
1525 lsb_control->set_float(float(new_patch.lsb), true, old_program.time);
1528 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1529 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1531 assert(program_control != 0);
1532 program_control->set_float(float(new_patch.program_number), true, old_program.time);
1538 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1540 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1541 alter_program_change(program_change_event, new_patch);
1545 MidiRegionView::previous_program(CanvasProgramChange& program)
1547 MIDI::Name::PatchPrimaryKey key;
1548 get_patch_key_at(program.event_time(), program.channel(), key);
1550 boost::shared_ptr<MIDI::Name::Patch> patch =
1551 MIDI::Name::MidiPatchManager::instance().previous_patch(
1553 _custom_device_mode,
1557 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1559 alter_program_change(program_change_event, patch->patch_primary_key());
1564 MidiRegionView::next_program(CanvasProgramChange& program)
1566 MIDI::Name::PatchPrimaryKey key;
1567 get_patch_key_at(program.event_time(), program.channel(), key);
1569 boost::shared_ptr<MIDI::Name::Patch> patch =
1570 MIDI::Name::MidiPatchManager::instance().next_patch(
1572 _custom_device_mode,
1576 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1578 alter_program_change(program_change_event, patch->patch_primary_key());
1583 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1585 if (_selection.empty()) {
1589 if (_selection.erase (cne) > 0) {
1590 cerr << "Erased a CNE from selection\n";
1595 MidiRegionView::delete_selection()
1597 if (_selection.empty()) {
1601 start_delta_command (_("delete selection"));
1603 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1604 if ((*i)->selected()) {
1605 _delta_command->remove((*i)->note());
1615 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1617 start_delta_command (_("delete note"));
1618 _delta_command->remove (n);
1621 trackview.editor().hide_verbose_canvas_cursor ();
1625 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1627 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1628 if ((*i)->selected() && (*i) != ev) {
1629 (*i)->selected(false);
1630 (*i)->hide_velocity();
1638 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1640 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1643 Selection::iterator tmp = i;
1646 (*i)->selected (false);
1647 _selection.erase (i);
1656 /* don't bother with removing this regionview from the editor selection,
1657 since we're about to add another note, and thus put/keep this
1658 regionview in the editor selection.
1661 if (!ev->selected()) {
1662 add_to_selection (ev);
1667 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1669 uint8_t low_note = 127;
1670 uint8_t high_note = 0;
1671 MidiModel::Notes& notes (_model->notes());
1672 _optimization_iterator = _events.begin();
1674 if (extend && _selection.empty()) {
1680 /* scan existing selection to get note range */
1682 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1683 if ((*i)->note()->note() < low_note) {
1684 low_note = (*i)->note()->note();
1686 if ((*i)->note()->note() > high_note) {
1687 high_note = (*i)->note()->note();
1691 low_note = min (low_note, notenum);
1692 high_note = max (high_note, notenum);
1695 no_sound_notes = true;
1697 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1699 boost::shared_ptr<NoteType> note (*n);
1700 CanvasNoteEvent* cne;
1701 bool select = false;
1703 if (((0x0001 << note->channel()) & channel_mask) != 0) {
1705 if ((note->note() >= low_note && note->note() <= high_note)) {
1708 } else if (note->note() == notenum) {
1714 if ((cne = find_canvas_note (note)) != 0) {
1715 // extend is false because we've taken care of it,
1716 // since it extends by time range, not pitch.
1717 note_selected (cne, add, false);
1721 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1725 no_sound_notes = false;
1729 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1731 MidiModel::Notes& notes (_model->notes());
1732 _optimization_iterator = _events.begin();
1734 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1736 boost::shared_ptr<NoteType> note (*n);
1737 CanvasNoteEvent* cne;
1739 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1740 if ((cne = find_canvas_note (note)) != 0) {
1741 if (cne->selected()) {
1742 note_deselected (cne);
1744 note_selected (cne, true, false);
1752 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1755 clear_selection_except(ev);
1760 if (!ev->selected()) {
1761 add_to_selection (ev);
1765 /* find end of latest note selected, select all between that and the start of "ev" */
1767 Evoral::MusicalTime earliest = DBL_MAX;
1768 Evoral::MusicalTime latest = 0;
1770 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1771 if ((*i)->note()->end_time() > latest) {
1772 latest = (*i)->note()->end_time();
1774 if ((*i)->note()->time() < earliest) {
1775 earliest = (*i)->note()->time();
1779 if (ev->note()->end_time() > latest) {
1780 latest = ev->note()->end_time();
1783 if (ev->note()->time() < earliest) {
1784 earliest = ev->note()->time();
1787 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1789 /* find notes entirely within OR spanning the earliest..latest range */
1791 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1792 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1793 add_to_selection (*i);
1797 /* if events were guaranteed to be time sorted, we could do this.
1798 but as of sept 10th 2009, they no longer are.
1801 if ((*i)->note()->time() > latest) {
1810 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1812 remove_from_selection (ev);
1816 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1826 // TODO: Make this faster by storing the last updated selection rect, and only
1827 // adjusting things that are in the area that appears/disappeared.
1828 // We probably need a tree to be able to find events in O(log(n)) time.
1830 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1832 /* check if any corner of the note is inside the rect
1835 1) this is computing "touched by", not "contained by" the rect.
1836 2) this does not require that events be sorted in time.
1839 const double ix1 = (*i)->x1();
1840 const double ix2 = (*i)->x2();
1841 const double iy1 = (*i)->y1();
1842 const double iy2 = (*i)->y2();
1844 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1845 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1846 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1847 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1850 if (!(*i)->selected()) {
1851 add_to_selection (*i);
1853 } else if ((*i)->selected()) {
1854 // Not inside rectangle
1855 remove_from_selection (*i);
1861 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1863 Selection::iterator i = _selection.find (ev);
1865 if (i != _selection.end()) {
1866 _selection.erase (i);
1869 ev->selected (false);
1870 ev->hide_velocity ();
1872 if (_selection.empty()) {
1873 PublicEditor& editor (trackview.editor());
1874 editor.get_selection().remove (this);
1879 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
1881 bool add_mrv_selection = false;
1883 if (_selection.empty()) {
1884 add_mrv_selection = true;
1887 if (_selection.insert (ev).second) {
1888 ev->selected (true);
1889 play_midi_note ((ev)->note());
1892 if (add_mrv_selection) {
1893 PublicEditor& editor (trackview.editor());
1894 editor.get_selection().add (this);
1899 MidiRegionView::move_selection(double dx, double dy)
1901 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1902 (*i)->move_event(dx, dy);
1907 MidiRegionView::note_dropped(CanvasNoteEvent *, double dt, int8_t dnote)
1909 assert (!_selection.empty());
1911 uint8_t lowest_note_in_selection = 127;
1912 uint8_t highest_note_in_selection = 0;
1913 uint8_t highest_note_difference = 0;
1915 // find highest and lowest notes first
1917 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1918 uint8_t pitch = (*i)->note()->note();
1919 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
1920 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
1924 cerr << "dnote: " << (int) dnote << endl;
1925 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
1926 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
1927 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
1928 << int(highest_note_in_selection) << endl;
1929 cerr << "selection size: " << _selection.size() << endl;
1930 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
1933 // Make sure the note pitch does not exceed the MIDI standard range
1934 if (highest_note_in_selection + dnote > 127) {
1935 highest_note_difference = highest_note_in_selection - 127;
1938 start_diff_command(_("move notes"));
1940 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
1942 nframes64_t start_frames = beats_to_frames((*i)->note()->time());
1945 start_frames += snap_frame_to_frame(trackview.editor().pixel_to_frame(dt));
1947 start_frames -= snap_frame_to_frame(trackview.editor().pixel_to_frame(-dt));
1950 Evoral::MusicalTime new_time = frames_to_beats(start_frames);
1956 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
1958 uint8_t original_pitch = (*i)->note()->note();
1959 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
1961 // keep notes in standard midi range
1962 clamp_to_0_127(new_pitch);
1964 // keep original pitch if note is dragged outside valid midi range
1965 if ((original_pitch != 0 && new_pitch == 0)
1966 || (original_pitch != 127 && new_pitch == 127)) {
1967 new_pitch = original_pitch;
1970 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
1971 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
1973 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
1978 // care about notes being moved beyond the upper/lower bounds on the canvas
1979 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
1980 highest_note_in_selection > midi_stream_view()->highest_note()) {
1981 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
1986 MidiRegionView::snap_pixel_to_frame(double x)
1988 PublicEditor& editor = trackview.editor();
1989 // x is region relative, convert it to global absolute frames
1990 nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
1991 editor.snap_to(frame);
1992 return frame - _region->position(); // convert back to region relative
1996 MidiRegionView::snap_frame_to_frame(nframes64_t x)
1998 PublicEditor& editor = trackview.editor();
1999 // x is region relative, convert it to global absolute frames
2000 nframes64_t frame = x + _region->position();
2001 editor.snap_to(frame);
2002 return frame - _region->position(); // convert back to region relative
2006 MidiRegionView::snap_to_pixel(double x)
2008 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2012 MidiRegionView::get_position_pixels()
2014 nframes64_t region_frame = get_position();
2015 return trackview.editor().frame_to_pixel(region_frame);
2019 MidiRegionView::get_end_position_pixels()
2021 nframes64_t frame = get_position() + get_duration ();
2022 return trackview.editor().frame_to_pixel(frame);
2026 MidiRegionView::beats_to_frames(double beats) const
2028 return _time_converter.to(beats);
2032 MidiRegionView::frames_to_beats(nframes64_t frames) const
2034 return _time_converter.from(frames);
2038 MidiRegionView::begin_resizing (bool /*at_front*/)
2040 _resize_data.clear();
2042 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2043 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2045 // only insert CanvasNotes into the map
2047 NoteResizeData *resize_data = new NoteResizeData();
2048 resize_data->canvas_note = note;
2050 // create a new SimpleRect from the note which will be the resize preview
2051 SimpleRect *resize_rect = new SimpleRect(
2052 *group, note->x1(), note->y1(), note->x2(), note->y2());
2054 // calculate the colors: get the color settings
2055 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2056 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2059 // make the resize preview notes more transparent and bright
2060 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2062 // calculate color based on note velocity
2063 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2064 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity()),
2068 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2069 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2071 resize_data->resize_rect = resize_rect;
2072 _resize_data.push_back(resize_data);
2078 MidiRegionView::update_resizing (bool at_front, double delta_x, bool relative)
2080 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2081 SimpleRect* resize_rect = (*i)->resize_rect;
2082 CanvasNote* canvas_note = (*i)->canvas_note;
2087 current_x = canvas_note->x1() + delta_x;
2089 // x is in track relative, transform it to region relative
2090 current_x = delta_x - get_position_pixels();
2094 current_x = canvas_note->x2() + delta_x;
2096 // x is in track relative, transform it to region relative
2097 current_x = delta_x - get_end_position_pixels ();
2102 resize_rect->property_x1() = snap_to_pixel(current_x);
2103 resize_rect->property_x2() = canvas_note->x2();
2105 resize_rect->property_x2() = snap_to_pixel(current_x);
2106 resize_rect->property_x1() = canvas_note->x1();
2112 MidiRegionView::commit_resizing (bool at_front, double delta_x, bool relative)
2114 start_diff_command(_("resize notes"));
2116 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2117 CanvasNote* canvas_note = (*i)->canvas_note;
2118 SimpleRect* resize_rect = (*i)->resize_rect;
2119 const double region_start = get_position_pixels();
2124 current_x = canvas_note->x1() + delta_x;
2126 // x is in track relative, transform it to region relative
2127 current_x = region_start + delta_x;
2131 current_x = canvas_note->x2() + delta_x;
2133 // x is in track relative, transform it to region relative
2134 current_x = region_start + delta_x;
2138 current_x = snap_pixel_to_frame (current_x);
2139 current_x = frames_to_beats (current_x);
2141 if (at_front && current_x < canvas_note->note()->end_time()) {
2142 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2144 double len = canvas_note->note()->time() - current_x;
2145 len += canvas_note->note()->length();
2148 /* XXX convert to beats */
2149 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2154 double len = current_x - canvas_note->note()->time();
2157 /* XXX convert to beats */
2158 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2166 _resize_data.clear();
2171 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2173 uint8_t new_velocity;
2176 new_velocity = event->note()->velocity() + velocity;
2177 clamp_to_0_127(new_velocity);
2179 new_velocity = velocity;
2182 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2186 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2191 new_note = event->note()->note() + note;
2196 clamp_to_0_127 (new_note);
2197 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2201 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2203 bool change_start = false;
2204 bool change_length = false;
2205 Evoral::MusicalTime new_start;
2206 Evoral::MusicalTime new_length;
2208 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2210 front_delta: if positive - move the start of the note later in time (shortening it)
2211 if negative - move the start of the note earlier in time (lengthening it)
2213 end_delta: if positive - move the end of the note later in time (lengthening it)
2214 if negative - move the end of the note earlier in time (shortening it)
2218 if (front_delta < 0) {
2220 if (event->note()->time() < -front_delta) {
2223 new_start = event->note()->time() + front_delta; // moves earlier
2226 /* start moved toward zero, so move the end point out to where it used to be.
2227 Note that front_delta is negative, so this increases the length.
2230 new_length = event->note()->length() - front_delta;
2231 change_start = true;
2232 change_length = true;
2236 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2238 if (new_pos < event->note()->end_time()) {
2239 new_start = event->note()->time() + front_delta;
2240 /* start moved toward the end, so move the end point back to where it used to be */
2241 new_length = event->note()->length() - front_delta;
2242 change_start = true;
2243 change_length = true;
2250 bool can_change = true;
2251 if (end_delta < 0) {
2252 if (event->note()->length() < -end_delta) {
2258 new_length = event->note()->length() + end_delta;
2259 change_length = true;
2264 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2267 if (change_length) {
2268 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2273 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2275 Evoral::MusicalTime new_time;
2279 if (event->note()->time() < -delta) {
2282 new_time = event->note()->time() + delta;
2285 new_time = event->note()->time() + delta;
2291 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2295 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2299 if (_selection.empty()) {
2314 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2315 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2321 start_diff_command(_("change velocities"));
2323 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2324 Selection::iterator next = i;
2326 change_note_velocity (*i, delta, true);
2335 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2337 if (_selection.empty()) {
2354 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2356 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2360 if ((int8_t) (*i)->note()->note() + delta > 127) {
2367 start_diff_command (_("transpose"));
2369 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2370 Selection::iterator next = i;
2372 change_note_note (*i, delta, true);
2380 MidiRegionView::change_note_lengths (bool fine, bool shorter, bool start, bool end)
2382 Evoral::MusicalTime delta;
2387 /* grab the current grid distance */
2389 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2391 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2392 cerr << "Grid type not available as beats - TO BE FIXED\n";
2401 start_diff_command (_("change note lengths"));
2403 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2404 Selection::iterator next = i;
2407 /* note the negation of the delta for start */
2409 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2418 MidiRegionView::nudge_notes (bool forward)
2420 if (_selection.empty()) {
2424 /* pick a note as the point along the timeline to get the nudge distance.
2425 its not necessarily the earliest note, so we may want to pull the notes out
2426 into a vector and sort before using the first one.
2429 nframes64_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2431 nframes64_t distance;
2433 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2435 /* grid is off - use nudge distance */
2437 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2443 nframes64_t next_pos = ref_point;
2446 /* XXX need check on max_frames, but that needs max_frames64 or something */
2449 if (next_pos == 0) {
2455 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2456 distance = ref_point - next_pos;
2459 if (distance == 0) {
2463 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2469 start_diff_command (_("nudge"));
2471 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2472 Selection::iterator next = i;
2474 change_note_time (*i, delta, true);
2482 MidiRegionView::change_channel(uint8_t channel)
2484 start_diff_command(_("change channel"));
2485 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2486 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2493 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2495 if (_mouse_state == SelectTouchDragging) {
2496 note_selected(ev, true);
2499 show_verbose_canvas_cursor (ev->note ());
2503 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2505 PublicEditor& editor (trackview.editor());
2506 editor.hide_verbose_canvas_cursor ();
2511 MidiRegionView::switch_source(boost::shared_ptr<Source> src)
2513 boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(src);
2515 display_model(msrc->model());
2519 MidiRegionView::set_frame_color()
2522 if (_selected && should_show_selection) {
2523 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2525 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2531 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2535 case FilterChannels:
2536 _force_channel = -1;
2539 _force_channel = mask;
2540 mask = 0xFFFF; // Show all notes as active (below)
2543 // Update notes for selection
2544 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2545 (*i)->on_channel_selection_change(mask);
2548 _last_channel_selection = mask;
2552 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2554 _model_name = model;
2555 _custom_device_mode = custom_device_mode;
2560 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2562 if (_selection.empty()) {
2566 PublicEditor& editor (trackview.editor());
2571 editor.get_cut_buffer().add (selection_as_cut_buffer());
2579 start_delta_command();
2581 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2587 delta_remove_note (*i);
2597 MidiRegionView::selection_as_cut_buffer () const
2601 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2602 NoteType* n = (*i)->note().get();
2603 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2606 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2613 MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
2619 start_delta_command (_("paste"));
2621 Evoral::MusicalTime beat_delta;
2622 Evoral::MusicalTime paste_pos_beats;
2623 Evoral::MusicalTime duration;
2624 Evoral::MusicalTime end_point;
2626 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2627 paste_pos_beats = frames_to_beats (pos - _region->position());
2628 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2629 paste_pos_beats = 0;
2631 _selection.clear ();
2633 for (int n = 0; n < (int) times; ++n) {
2635 cerr << "Pasting " << mcb.notes().size() << " for the " << n+1 << "th time\n";
2637 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2639 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2640 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2642 /* make all newly added notes selected */
2644 delta_add_note (copied_note, true);
2645 end_point = copied_note->end_time();
2648 paste_pos_beats += duration;
2651 /* if we pasted past the current end of the region, extend the region */
2653 nframes64_t end_frame = _region->position() + beats_to_frames (end_point);
2654 nframes64_t region_end = _region->position() + _region->length() - 1;
2656 if (end_frame > region_end) {
2658 cerr << "region end is now " << end_frame << " to extend from " << region_end << endl;
2660 trackview.session()->begin_reversible_command (_("paste"));
2662 _region->clear_history ();
2663 _region->set_length (end_frame, this);
2664 trackview.session()->add_command (new StatefulDiffCommand (_region));
2667 cerr << "region end finally at " << _region->position() + _region->length() - 1;
2671 struct EventNoteTimeEarlyFirstComparator {
2672 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2673 return a->note()->time() < b->note()->time();
2678 MidiRegionView::time_sort_events ()
2680 if (!_sort_needed) {
2684 EventNoteTimeEarlyFirstComparator cmp;
2687 _sort_needed = false;
2691 MidiRegionView::goto_next_note ()
2693 // nframes64_t pos = -1;
2694 bool use_next = false;
2696 if (_events.back()->selected()) {
2700 time_sort_events ();
2702 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2703 if ((*i)->selected()) {
2706 } else if (use_next) {
2708 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2713 /* use the first one */
2715 unique_select (_events.front());
2720 MidiRegionView::goto_previous_note ()
2722 // nframes64_t pos = -1;
2723 bool use_next = false;
2725 if (_events.front()->selected()) {
2729 time_sort_events ();
2731 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2732 if ((*i)->selected()) {
2735 } else if (use_next) {
2737 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2742 /* use the last one */
2744 unique_select (*(_events.rbegin()));
2748 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2750 bool had_selected = false;
2752 time_sort_events ();
2754 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2755 if ((*i)->selected()) {
2756 selected.insert ((*i)->note());
2757 had_selected = true;
2761 if (allow_all_if_none_selected && !had_selected) {
2762 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2763 selected.insert ((*i)->note());
2769 MidiRegionView::update_ghost_note (double x, double y)
2775 nframes64_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
2776 trackview.editor().snap_to (f);
2777 f -= _region->position ();
2780 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
2785 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
2787 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
2788 _ghost_note->note()->set_length (length);
2789 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
2791 update_note (_ghost_note);
2793 show_verbose_canvas_cursor (_ghost_note->note ());
2797 MidiRegionView::create_ghost_note (double x, double y)
2802 boost::shared_ptr<NoteType> g (new NoteType);
2803 _ghost_note = new NoEventCanvasNote (*this, *group, g);
2804 update_ghost_note (x, y);
2805 _ghost_note->show ();
2810 show_verbose_canvas_cursor (_ghost_note->note ());
2814 MidiRegionView::snap_changed ()
2820 create_ghost_note (_last_ghost_x, _last_ghost_y);
2824 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
2827 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (n->note()).c_str(), (int) n->note ());
2828 trackview.editor().show_verbose_canvas_cursor_with (buf);