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))
92 , _optimization_iterator (_events.end())
94 , no_sound_notes (false)
96 _note_group->raise_to_top();
99 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
100 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
101 TimeAxisViewItem::Visibility visibility)
102 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
104 , _last_channel_selection(0xFFFF)
105 , _model_name(string())
106 , _custom_device_mode(string())
108 , _note_group(new ArdourCanvas::Group(*parent))
114 , _sort_needed (true)
115 , _optimization_iterator (_events.end())
117 , no_sound_notes (false)
120 _note_group->raise_to_top();
124 MidiRegionView::MidiRegionView (const MidiRegionView& other)
125 : sigc::trackable(other)
128 , _last_channel_selection(0xFFFF)
129 , _model_name(string())
130 , _custom_device_mode(string())
132 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
138 , _sort_needed (true)
139 , _optimization_iterator (_events.end())
141 , no_sound_notes (false)
146 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
147 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
152 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
153 : RegionView (other, boost::shared_ptr<Region> (region))
155 , _last_channel_selection(0xFFFF)
156 , _model_name(string())
157 , _custom_device_mode(string())
159 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
165 , _sort_needed (true)
166 , _optimization_iterator (_events.end())
168 , no_sound_notes (false)
173 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
174 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
180 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
182 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
183 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
187 midi_region()->midi_source(0)->load_model();
190 _model = midi_region()->midi_source(0)->model();
191 _enable_display = false;
193 RegionView::init (basic_color, false);
195 compute_colors (basic_color);
197 set_height (trackview.current_height());
200 region_sync_changed ();
201 region_resized (ARDOUR::bounds_change);
204 reset_width_dependent_items (_pixel_width);
208 _enable_display = true;
211 display_model (_model);
215 group->raise_to_top();
216 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
218 midi_view()->signal_channel_mode_changed().connect(
219 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
221 midi_view()->signal_midi_patch_settings_changed().connect(
222 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
224 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
228 MidiRegionView::canvas_event(GdkEvent* ev)
230 PublicEditor& editor (trackview.editor());
232 if (!editor.internal_editing()) {
236 static double drag_start_x, drag_start_y;
237 static double last_x, last_y;
238 double event_x, event_y;
239 nframes64_t event_frame = 0;
242 static ArdourCanvas::SimpleRect* drag_rect = 0;
244 /* XXX: note that as of August 2009, the GnomeCanvas does not propagate scroll events
245 to its items, which means that ev->type == GDK_SCROLL will never be seen
250 fine = Keyboard::modifier_state_equals (ev->scroll.state, Keyboard::Level4Modifier);
252 if (ev->scroll.direction == GDK_SCROLL_UP) {
253 change_velocities (true, fine, false);
255 } else if (ev->scroll.direction == GDK_SCROLL_DOWN) {
256 change_velocities (false, fine, false);
265 /* since GTK bindings are generally activated on press, and since
266 detectable auto-repeat is the name of the game and only sends
267 repeated presses, carry out key actions at key press, not release.
270 if (ev->key.keyval == GDK_Alt_L || ev->key.keyval == GDK_Alt_R){
271 _mouse_state = SelectTouchDragging;
274 } else if (ev->key.keyval == GDK_Escape) {
278 } else if (ev->key.keyval == GDK_comma || ev->key.keyval == GDK_period) {
280 bool start = (ev->key.keyval == GDK_comma);
281 bool end = (ev->key.keyval == GDK_period);
282 bool shorter = Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier);
283 fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier);
285 change_note_lengths (fine, shorter, start, end);
289 } else if (ev->key.keyval == GDK_Delete) {
294 } else if (ev->key.keyval == GDK_Tab) {
296 if (Keyboard::modifier_state_equals (ev->key.state, Keyboard::PrimaryModifier)) {
297 goto_previous_note ();
303 } else if (ev->key.keyval == GDK_Up) {
305 bool allow_smush = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier);
306 bool fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::TertiaryModifier);
308 if (Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier)) {
309 change_velocities (true, fine, allow_smush);
311 transpose (true, fine, allow_smush);
315 } else if (ev->key.keyval == GDK_Down) {
317 bool allow_smush = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier);
318 fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::TertiaryModifier);
320 if (Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier)) {
321 change_velocities (false, fine, allow_smush);
323 transpose (false, fine, allow_smush);
327 } else if (ev->key.keyval == GDK_Left) {
332 } else if (ev->key.keyval == GDK_Right) {
337 } else if (ev->key.keyval == GDK_Control_L) {
340 } else if (ev->key.keyval == GDK_r) {
341 /* if we're not step editing, this really doesn't matter */
342 midi_view()->step_edit_rest ();
348 case GDK_KEY_RELEASE:
349 if (ev->key.keyval == GDK_Alt_L || ev->key.keyval == GDK_Alt_R) {
355 case GDK_BUTTON_PRESS:
356 last_x = ev->button.x;
357 last_y = ev->button.y;
358 group->w2i (last_x, last_y);
360 if (_mouse_state != SelectTouchDragging && ev->button.button == 1) {
361 _pressed_button = ev->button.button;
362 _mouse_state = Pressed;
365 _pressed_button = ev->button.button;
368 case GDK_2BUTTON_PRESS:
371 case GDK_ENTER_NOTIFY:
373 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
374 Keyboard::magic_widget_grab_focus();
377 if (editor.current_mouse_mode() == MouseRange) {
378 create_ghost_note (ev->crossing.x, ev->crossing.y);
383 case GDK_LEAVE_NOTIFY:
385 trackview.editor().hide_verbose_canvas_cursor ();
391 case GDK_MOTION_NOTIFY:
393 event_x = ev->motion.x;
394 event_y = ev->motion.y;
395 group->w2i(event_x, event_y);
397 // convert event_x to global frame
398 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
399 trackview.editor().snap_to(event_frame);
400 // convert event_frame back to local coordinates relative to position
401 event_frame -= _region->position();
404 update_ghost_note (ev->motion.x, ev->motion.y);
407 switch (_mouse_state) {
408 case Pressed: // Maybe start a drag, if we've moved a bit
410 if (fabs (event_x - last_x) < 1 && fabs (event_y - last_y) < 1) {
411 /* no appreciable movement since the button was pressed */
416 if (_pressed_button == 1 && editor.current_mouse_mode() == MouseObject) {
417 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
418 Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
421 drag_start_x = event_x;
422 drag_start_y = event_y;
424 drag_rect = new ArdourCanvas::SimpleRect(*group);
425 drag_rect->property_x1() = event_x;
426 drag_rect->property_y1() = event_y;
427 drag_rect->property_x2() = event_x;
428 drag_rect->property_y2() = event_y;
429 drag_rect->property_outline_what() = 0xFF;
430 drag_rect->property_outline_color_rgba()
431 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
432 drag_rect->property_fill_color_rgba()
433 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
435 _mouse_state = SelectRectDragging;
438 // Add note drag start
439 } else if (editor.internal_editing()) {
440 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
441 Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
444 drag_start_x = event_x;
445 drag_start_y = event_y;
447 drag_rect = new ArdourCanvas::SimpleRect(*group);
448 drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
450 drag_rect->property_y1() = midi_stream_view()->note_to_y(
451 midi_stream_view()->y_to_note(event_y));
452 drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
453 drag_rect->property_y2() = drag_rect->property_y1()
454 + floor(midi_stream_view()->note_height());
455 drag_rect->property_outline_what() = 0xFF;
456 drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
457 drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
459 _mouse_state = AddDragging;
465 case SelectRectDragging: // Select drag motion
466 case AddDragging: // Add note drag motion
467 if (ev->motion.is_hint) {
470 GdkModifierType state;
471 gdk_window_get_pointer(ev->motion.window, &t_x, &t_y, &state);
476 if (_mouse_state == AddDragging)
477 event_x = trackview.editor().frame_to_pixel(event_frame);
480 if (event_x > drag_start_x)
481 drag_rect->property_x2() = event_x;
483 drag_rect->property_x1() = event_x;
486 if (drag_rect && _mouse_state == SelectRectDragging) {
487 if (event_y > drag_start_y)
488 drag_rect->property_y2() = event_y;
490 drag_rect->property_y1() = event_y;
492 update_drag_selection(drag_start_x, event_x, drag_start_y, event_y);
498 case SelectTouchDragging:
507 case GDK_BUTTON_RELEASE:
508 event_x = ev->motion.x;
509 event_y = ev->motion.y;
510 group->w2i(event_x, event_y);
511 group->ungrab(ev->button.time);
512 event_frame = trackview.editor().pixel_to_frame(event_x);
514 if (ev->button.button == 3) {
516 } else if (_pressed_button != 1) {
520 switch (_mouse_state) {
521 case Pressed: // Clicked
522 switch (editor.current_mouse_mode()) {
530 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
535 create_note_at (event_x, event_y, beats);
543 case SelectRectDragging: // Select drag done
548 case AddDragging: // Add drag done
550 if (drag_rect->property_x2() > drag_rect->property_x1() + 2) {
551 const double x = drag_rect->property_x1();
552 const double length = trackview.editor().pixel_to_frame(
553 drag_rect->property_x2() - drag_rect->property_x1());
555 create_note_at(x, drag_rect->property_y1(), frames_to_beats(length));
570 MidiRegionView::show_list_editor ()
573 _list_editor = new MidiListEditor (trackview.session(), midi_region());
575 _list_editor->present ();
578 /** Add a note to the model, and the view, at a canvas (click) coordinate.
579 * \param x horizontal position in pixels
580 * \param y vertical position in pixels
581 * \param length duration of the note in beats */
583 MidiRegionView::create_note_at(double x, double y, double length)
585 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
586 MidiStreamView* const view = mtv->midi_view();
588 double note = midi_stream_view()->y_to_note(y);
591 assert(note <= 127.0);
593 // Start of note in frames relative to region start
594 nframes64_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
595 assert(start_frames >= 0);
598 length = frames_to_beats(
599 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
601 assert (length != 0);
603 const boost::shared_ptr<NoteType> new_note(new NoteType(0,
604 frames_to_beats(start_frames + _region->start()), length,
605 (uint8_t)note, 0x40));
607 if (_model->contains (new_note)) {
611 view->update_note_range(new_note->note());
613 MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
615 _model->apply_command(*trackview.session(), cmd);
617 play_midi_note (new_note);
621 MidiRegionView::clear_events()
626 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
627 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
632 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
637 _pgm_changes.clear();
639 _optimization_iterator = _events.end();
644 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
647 content_connection.disconnect ();
648 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
652 if (_enable_display) {
659 MidiRegionView::start_delta_command(string name)
661 if (!_delta_command) {
662 _delta_command = _model->new_delta_command(name);
667 MidiRegionView::start_diff_command(string name)
669 if (!_diff_command) {
670 _diff_command = _model->new_diff_command(name);
675 MidiRegionView::delta_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
677 if (_delta_command) {
678 _delta_command->add(note);
681 _marked_for_selection.insert(note);
684 _marked_for_velocity.insert(note);
689 MidiRegionView::delta_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
691 if (_delta_command && ev->note()) {
692 _delta_command->remove(ev->note());
697 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
698 MidiModel::DiffCommand::Property property,
702 _diff_command->change (ev->note(), property, val);
707 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
708 MidiModel::DiffCommand::Property property,
709 Evoral::MusicalTime val)
712 _diff_command->change (ev->note(), property, val);
717 MidiRegionView::apply_delta()
719 if (!_delta_command) {
723 // Mark all selected notes for selection when model reloads
724 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
725 _marked_for_selection.insert((*i)->note());
728 _model->apply_command(*trackview.session(), _delta_command);
730 midi_view()->midi_track()->playlist_modified();
732 _marked_for_selection.clear();
733 _marked_for_velocity.clear();
737 MidiRegionView::apply_diff ()
739 if (!_diff_command) {
743 _model->apply_command(*trackview.session(), _diff_command);
745 midi_view()->midi_track()->playlist_modified();
747 _marked_for_velocity.clear();
751 MidiRegionView::apply_delta_as_subcommand()
753 if (!_delta_command) {
757 // Mark all selected notes for selection when model reloads
758 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
759 _marked_for_selection.insert((*i)->note());
762 _model->apply_command_as_subcommand(*trackview.session(), _delta_command);
764 midi_view()->midi_track()->playlist_modified();
766 _marked_for_selection.clear();
767 _marked_for_velocity.clear();
771 MidiRegionView::apply_diff_as_subcommand()
773 if (!_diff_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_as_subcommand(*trackview.session(), _diff_command);
784 midi_view()->midi_track()->playlist_modified();
786 _marked_for_selection.clear();
787 _marked_for_velocity.clear();
791 MidiRegionView::abort_command()
793 delete _delta_command;
795 delete _diff_command;
801 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
803 if (_optimization_iterator != _events.end()) {
804 ++_optimization_iterator;
807 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
808 return *_optimization_iterator;
811 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
812 if ((*_optimization_iterator)->note() == note) {
813 return *_optimization_iterator;
821 MidiRegionView::redisplay_model()
823 // Don't redisplay the model if we're currently recording and displaying that
829 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
833 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
837 MidiModel::ReadLock lock(_model->read_lock());
839 MidiModel::Notes& notes (_model->notes());
840 _optimization_iterator = _events.begin();
842 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
844 boost::shared_ptr<NoteType> note (*n);
845 CanvasNoteEvent* cne;
848 if (note_in_region_range (note, visible)) {
850 if ((cne = find_canvas_note (note)) != 0) {
857 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
859 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
871 add_note (note, visible);
876 if ((cne = find_canvas_note (note)) != 0) {
884 /* remove note items that are no longer valid */
886 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
887 if (!(*i)->valid ()) {
889 i = _events.erase (i);
896 display_program_changes();
898 _marked_for_selection.clear ();
899 _marked_for_velocity.clear ();
901 /* we may have caused _events to contain things out of order (e.g. if a note
902 moved earlier or later). we don't generally need them in time order, but
903 make a note that a sort is required for those cases that require it.
910 MidiRegionView::display_program_changes()
912 boost::shared_ptr<Evoral::Control> control = _model->control(MidiPgmChangeAutomation);
917 Glib::Mutex::Lock lock (control->list()->lock());
919 uint8_t channel = control->parameter().channel();
921 for (AutomationList::const_iterator event = control->list()->begin();
922 event != control->list()->end(); ++event) {
923 double event_time = (*event)->when;
924 double program_number = floor((*event)->value + 0.5);
926 // Get current value of bank select MSB at time of the program change
927 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
928 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
930 if (msb_control != 0) {
931 msb = uint8_t(floor(msb_control->get_float(true, event_time) + 0.5));
934 // Get current value of bank select LSB at time of the program change
935 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
936 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
938 if (lsb_control != 0) {
939 lsb = uint8_t(floor(lsb_control->get_float(true, event_time) + 0.5));
942 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
944 boost::shared_ptr<MIDI::Name::Patch> patch =
945 MIDI::Name::MidiPatchManager::instance().find_patch(
946 _model_name, _custom_device_mode, channel, patch_key);
948 PCEvent program_change(event_time, uint8_t(program_number), channel);
951 add_pgm_change(program_change, patch->name());
954 snprintf(buf, 4, "%d", int(program_number));
955 add_pgm_change(program_change, buf);
961 MidiRegionView::display_sysexes()
963 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
964 Evoral::MusicalTime time = (*i)->time();
969 for (uint32_t b = 0; b < (*i)->size(); ++b) {
970 str << int((*i)->buffer()[b]);
971 if (b != (*i)->size() -1) {
975 string text = str.str();
977 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
979 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
981 double height = midi_stream_view()->contents_height();
983 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
984 new CanvasSysEx(*this, *group, text, height, x, 1.0));
986 // Show unless program change is beyond the region bounds
987 if (time - _region->start() >= _region->length() || time < _region->start()) {
993 _sys_exes.push_back(sysex);
998 MidiRegionView::~MidiRegionView ()
1000 in_destructor = true;
1002 note_delete_connection.disconnect ();
1004 delete _list_editor;
1006 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1008 if (_active_notes) {
1015 delete _delta_command;
1019 MidiRegionView::region_resized (const PropertyChange& what_changed)
1021 RegionView::region_resized(what_changed);
1023 if (what_changed.contains (ARDOUR::Properties::position)) {
1024 set_duration(_region->length(), 0);
1025 if (_enable_display) {
1032 MidiRegionView::reset_width_dependent_items (double pixel_width)
1034 RegionView::reset_width_dependent_items(pixel_width);
1035 assert(_pixel_width == pixel_width);
1037 if (_enable_display) {
1043 MidiRegionView::set_height (double height)
1045 static const double FUDGE = 2.0;
1046 const double old_height = _height;
1047 RegionView::set_height(height);
1048 _height = height - FUDGE;
1050 apply_note_range(midi_stream_view()->lowest_note(),
1051 midi_stream_view()->highest_note(),
1052 height != old_height + FUDGE);
1055 name_pixbuf->raise_to_top();
1060 /** Apply the current note range from the stream view
1061 * by repositioning/hiding notes as necessary
1064 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1066 if (!_enable_display) {
1070 if (!force && _current_range_min == min && _current_range_max == max) {
1074 _current_range_min = min;
1075 _current_range_max = max;
1077 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1078 CanvasNoteEvent* event = *i;
1079 boost::shared_ptr<NoteType> note (event->note());
1081 if (note->note() < _current_range_min ||
1082 note->note() > _current_range_max) {
1088 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1090 const double y1 = midi_stream_view()->note_to_y(note->note());
1091 const double y2 = y1 + floor(midi_stream_view()->note_height());
1093 cnote->property_y1() = y1;
1094 cnote->property_y2() = y2;
1096 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1098 double x = trackview.editor().frame_to_pixel(
1099 beats_to_frames(note->time()) - _region->start());
1100 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1101 double y = midi_stream_view()->note_to_y(event->note()->note())
1102 + ((diamond_size-2.0) / 4.0);
1104 chit->set_height (diamond_size);
1105 chit->move (x - chit->x1(), y - chit->y1());
1112 MidiRegionView::add_ghost (TimeAxisView& tv)
1116 double unit_position = _region->position () / samples_per_unit;
1117 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1118 MidiGhostRegion* ghost;
1120 if (mtv && mtv->midi_view()) {
1121 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1122 to allow having midi notes on top of note lines and waveforms.
1124 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1126 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1129 ghost->set_height ();
1130 ghost->set_duration (_region->length() / samples_per_unit);
1131 ghosts.push_back (ghost);
1133 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1134 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1135 ghost->add_note(note);
1139 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1145 /** Begin tracking note state for successive calls to add_event
1148 MidiRegionView::begin_write()
1150 assert(!_active_notes);
1151 _active_notes = new CanvasNote*[128];
1152 for (unsigned i=0; i < 128; ++i) {
1153 _active_notes[i] = 0;
1158 /** Destroy note state for add_event
1161 MidiRegionView::end_write()
1163 delete[] _active_notes;
1165 _marked_for_selection.clear();
1166 _marked_for_velocity.clear();
1170 /** Resolve an active MIDI note (while recording).
1173 MidiRegionView::resolve_note(uint8_t note, double end_time)
1175 if (midi_view()->note_mode() != Sustained) {
1179 if (_active_notes && _active_notes[note]) {
1180 const nframes64_t end_time_frames = beats_to_frames(end_time);
1181 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1182 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1183 _active_notes[note] = 0;
1188 /** Extend active notes to rightmost edge of region (if length is changed)
1191 MidiRegionView::extend_active_notes()
1193 if (!_active_notes) {
1197 for (unsigned i=0; i < 128; ++i) {
1198 if (_active_notes[i]) {
1199 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1205 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1207 if (no_sound_notes || !trackview.editor().sound_notes()) {
1211 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1214 route_ui->midi_track()->write_immediate_event(
1215 note->on_event().size(), note->on_event().buffer());
1217 const double note_length_beats = (note->off_event().time() - note->on_event().time());
1218 nframes_t note_length_ms = beats_to_frames(note_length_beats)
1219 * (1000 / (double)route_ui->session()->nominal_frame_rate());
1220 Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(this, &MidiRegionView::play_midi_note_off), note),
1221 note_length_ms, G_PRIORITY_DEFAULT);
1225 MidiRegionView::play_midi_note_off(boost::shared_ptr<NoteType> note)
1227 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1230 route_ui->midi_track()->write_immediate_event(
1231 note->off_event().size(), note->off_event().buffer());
1237 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1239 const nframes64_t note_start_frames = beats_to_frames(note->time());
1241 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1242 (note_start_frames < _region->start());
1244 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1245 (note->note() <= midi_stream_view()->highest_note());
1251 MidiRegionView::update_note (CanvasNote* ev)
1253 boost::shared_ptr<NoteType> note = ev->note();
1255 const nframes64_t note_start_frames = beats_to_frames(note->time());
1256 const nframes64_t note_end_frames = beats_to_frames(note->end_time());
1258 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1259 const double y1 = midi_stream_view()->note_to_y(note->note());
1260 const double note_endpixel =
1261 trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1263 ev->property_x1() = x;
1264 ev->property_y1() = y1;
1265 if (note->length() > 0) {
1266 ev->property_x2() = note_endpixel;
1268 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1270 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1272 if (note->length() == 0) {
1273 if (_active_notes) {
1274 assert(note->note() < 128);
1275 // If this note is already active there's a stuck note,
1276 // finish the old note rectangle
1277 if (_active_notes[note->note()]) {
1278 CanvasNote* const old_rect = _active_notes[note->note()];
1279 boost::shared_ptr<NoteType> old_note = old_rect->note();
1280 old_rect->property_x2() = x;
1281 old_rect->property_outline_what() = (guint32) 0xF;
1283 _active_notes[note->note()] = ev;
1285 /* outline all but right edge */
1286 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1288 /* outline all edges */
1289 ev->property_outline_what() = (guint32) 0xF;
1294 MidiRegionView::update_hit (CanvasHit* ev)
1296 boost::shared_ptr<NoteType> note = ev->note();
1298 const nframes64_t note_start_frames = beats_to_frames(note->time());
1299 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1300 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1301 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1306 /** Add a MIDI note to the view (with length).
1308 * If in sustained mode, notes with length 0 will be considered active
1309 * notes, and resolve_note should be called when the corresponding note off
1310 * event arrives, to properly display the note.
1313 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1315 CanvasNoteEvent* event = 0;
1317 assert(note->time() >= 0);
1318 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1320 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1322 if (midi_view()->note_mode() == Sustained) {
1324 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1326 update_note (ev_rect);
1330 MidiGhostRegion* gr;
1332 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1333 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1334 gr->add_note(ev_rect);
1338 } else if (midi_view()->note_mode() == Percussive) {
1340 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1342 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1344 update_hit (ev_diamond);
1353 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1354 note_selected(event, true);
1357 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1358 event->show_velocity();
1360 event->on_channel_selection_change(_last_channel_selection);
1361 _events.push_back(event);
1372 MidiRegionView::add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1373 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1375 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1377 start_delta_command (_("step add"));
1378 delta_add_note (new_note, true, false);
1381 /* potentially extend region to hold new note */
1383 nframes64_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1384 nframes64_t region_end = _region->position() + _region->length() - 1;
1386 if (end_frame > region_end) {
1387 _region->set_length (end_frame, this);
1394 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1396 assert(program.time >= 0);
1398 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1399 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1401 double height = midi_stream_view()->contents_height();
1403 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1404 new CanvasProgramChange(*this, *group,
1409 _custom_device_mode,
1410 program.time, program.channel, program.value));
1412 // Show unless program change is beyond the region bounds
1413 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1419 _pgm_changes.push_back(pgm_change);
1423 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1425 cerr << "getting patch key at " << time << " for channel " << channel << endl;
1426 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1427 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1429 if (msb_control != 0) {
1430 msb = int(msb_control->get_float(true, time));
1431 cerr << "got msb " << msb;
1434 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1435 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1437 if (lsb_control != 0) {
1438 lsb = lsb_control->get_float(true, time);
1439 cerr << " got lsb " << lsb;
1442 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1443 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1444 float program_number = -1.0;
1445 if (program_control != 0) {
1446 program_number = program_control->get_float(true, time);
1447 cerr << " got program " << program_number << endl;
1450 key.msb = (int) floor(msb + 0.5);
1451 key.lsb = (int) floor(lsb + 0.5);
1452 key.program_number = (int) floor(program_number + 0.5);
1453 assert(key.is_sane());
1458 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1460 // TODO: Get the real event here and alter them at the original times
1461 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1462 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1463 if (msb_control != 0) {
1464 msb_control->set_float(float(new_patch.msb), true, old_program.time);
1467 // TODO: Get the real event here and alter them at the original times
1468 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1469 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1470 if (lsb_control != 0) {
1471 lsb_control->set_float(float(new_patch.lsb), true, old_program.time);
1474 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1475 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1477 assert(program_control != 0);
1478 program_control->set_float(float(new_patch.program_number), true, old_program.time);
1484 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1486 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1487 alter_program_change(program_change_event, new_patch);
1491 MidiRegionView::previous_program(CanvasProgramChange& program)
1493 MIDI::Name::PatchPrimaryKey key;
1494 get_patch_key_at(program.event_time(), program.channel(), key);
1496 boost::shared_ptr<MIDI::Name::Patch> patch =
1497 MIDI::Name::MidiPatchManager::instance().previous_patch(
1499 _custom_device_mode,
1503 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1505 alter_program_change(program_change_event, patch->patch_primary_key());
1510 MidiRegionView::next_program(CanvasProgramChange& program)
1512 MIDI::Name::PatchPrimaryKey key;
1513 get_patch_key_at(program.event_time(), program.channel(), key);
1515 boost::shared_ptr<MIDI::Name::Patch> patch =
1516 MIDI::Name::MidiPatchManager::instance().next_patch(
1518 _custom_device_mode,
1522 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1524 alter_program_change(program_change_event, patch->patch_primary_key());
1529 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1531 if (_selection.empty()) {
1535 if (_selection.erase (cne) > 0) {
1536 cerr << "Erased a CNE from selection\n";
1541 MidiRegionView::delete_selection()
1543 if (_selection.empty()) {
1547 start_delta_command (_("delete selection"));
1549 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1550 if ((*i)->selected()) {
1551 _delta_command->remove((*i)->note());
1561 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1563 start_delta_command (_("delete note"));
1564 _delta_command->remove (n);
1567 trackview.editor().hide_verbose_canvas_cursor ();
1571 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1573 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1574 if ((*i)->selected() && (*i) != ev) {
1575 (*i)->selected(false);
1576 (*i)->hide_velocity();
1584 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1586 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1589 Selection::iterator tmp = i;
1592 (*i)->selected (false);
1593 _selection.erase (i);
1602 /* don't bother with removing this regionview from the editor selection,
1603 since we're about to add another note, and thus put/keep this
1604 regionview in the editor selection.
1607 if (!ev->selected()) {
1608 add_to_selection (ev);
1613 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1615 uint8_t low_note = 127;
1616 uint8_t high_note = 0;
1617 MidiModel::Notes& notes (_model->notes());
1618 _optimization_iterator = _events.begin();
1620 if (extend && _selection.empty()) {
1626 /* scan existing selection to get note range */
1628 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1629 if ((*i)->note()->note() < low_note) {
1630 low_note = (*i)->note()->note();
1632 if ((*i)->note()->note() > high_note) {
1633 high_note = (*i)->note()->note();
1637 low_note = min (low_note, notenum);
1638 high_note = max (high_note, notenum);
1641 no_sound_notes = true;
1643 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1645 boost::shared_ptr<NoteType> note (*n);
1646 CanvasNoteEvent* cne;
1647 bool select = false;
1649 if (((0x0001 << note->channel()) & channel_mask) != 0) {
1651 if ((note->note() >= low_note && note->note() <= high_note)) {
1654 } else if (note->note() == notenum) {
1660 if ((cne = find_canvas_note (note)) != 0) {
1661 // extend is false because we've taken care of it,
1662 // since it extends by time range, not pitch.
1663 note_selected (cne, add, false);
1667 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1671 no_sound_notes = false;
1675 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1677 MidiModel::Notes& notes (_model->notes());
1678 _optimization_iterator = _events.begin();
1680 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1682 boost::shared_ptr<NoteType> note (*n);
1683 CanvasNoteEvent* cne;
1685 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1686 if ((cne = find_canvas_note (note)) != 0) {
1687 if (cne->selected()) {
1688 note_deselected (cne);
1690 note_selected (cne, true, false);
1698 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1701 clear_selection_except(ev);
1706 if (!ev->selected()) {
1707 add_to_selection (ev);
1711 /* find end of latest note selected, select all between that and the start of "ev" */
1713 Evoral::MusicalTime earliest = DBL_MAX;
1714 Evoral::MusicalTime latest = 0;
1716 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1717 if ((*i)->note()->end_time() > latest) {
1718 latest = (*i)->note()->end_time();
1720 if ((*i)->note()->time() < earliest) {
1721 earliest = (*i)->note()->time();
1725 if (ev->note()->end_time() > latest) {
1726 latest = ev->note()->end_time();
1729 if (ev->note()->time() < earliest) {
1730 earliest = ev->note()->time();
1733 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1735 /* find notes entirely within OR spanning the earliest..latest range */
1737 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1738 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1739 add_to_selection (*i);
1743 /* if events were guaranteed to be time sorted, we could do this.
1744 but as of sept 10th 2009, they no longer are.
1747 if ((*i)->note()->time() > latest) {
1756 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1758 remove_from_selection (ev);
1762 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1772 // TODO: Make this faster by storing the last updated selection rect, and only
1773 // adjusting things that are in the area that appears/disappeared.
1774 // We probably need a tree to be able to find events in O(log(n)) time.
1776 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1778 /* check if any corner of the note is inside the rect
1781 1) this is computing "touched by", not "contained by" the rect.
1782 2) this does not require that events be sorted in time.
1785 const double ix1 = (*i)->x1();
1786 const double ix2 = (*i)->x2();
1787 const double iy1 = (*i)->y1();
1788 const double iy2 = (*i)->y2();
1790 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1791 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1792 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1793 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1796 if (!(*i)->selected()) {
1797 add_to_selection (*i);
1799 } else if ((*i)->selected()) {
1800 // Not inside rectangle
1801 remove_from_selection (*i);
1807 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1809 Selection::iterator i = _selection.find (ev);
1811 if (i != _selection.end()) {
1812 _selection.erase (i);
1815 ev->selected (false);
1816 ev->hide_velocity ();
1818 if (_selection.empty()) {
1819 PublicEditor& editor (trackview.editor());
1820 editor.get_selection().remove (this);
1825 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
1827 bool add_mrv_selection = false;
1829 if (_selection.empty()) {
1830 add_mrv_selection = true;
1833 if (_selection.insert (ev).second) {
1834 ev->selected (true);
1835 play_midi_note ((ev)->note());
1838 if (add_mrv_selection) {
1839 PublicEditor& editor (trackview.editor());
1840 editor.get_selection().add (this);
1845 MidiRegionView::move_selection(double dx, double dy)
1847 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1848 (*i)->move_event(dx, dy);
1853 MidiRegionView::note_dropped(CanvasNoteEvent *, double dt, int8_t dnote)
1855 assert (!_selection.empty());
1857 uint8_t lowest_note_in_selection = 127;
1858 uint8_t highest_note_in_selection = 0;
1859 uint8_t highest_note_difference = 0;
1861 // find highest and lowest notes first
1863 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1864 uint8_t pitch = (*i)->note()->note();
1865 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
1866 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
1870 cerr << "dnote: " << (int) dnote << endl;
1871 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
1872 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
1873 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
1874 << int(highest_note_in_selection) << endl;
1875 cerr << "selection size: " << _selection.size() << endl;
1876 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
1879 // Make sure the note pitch does not exceed the MIDI standard range
1880 if (highest_note_in_selection + dnote > 127) {
1881 highest_note_difference = highest_note_in_selection - 127;
1884 start_diff_command(_("move notes"));
1886 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
1888 nframes64_t start_frames = beats_to_frames((*i)->note()->time());
1891 start_frames += snap_frame_to_frame(trackview.editor().pixel_to_frame(dt));
1893 start_frames -= snap_frame_to_frame(trackview.editor().pixel_to_frame(-dt));
1896 Evoral::MusicalTime new_time = frames_to_beats(start_frames);
1902 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
1904 uint8_t original_pitch = (*i)->note()->note();
1905 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
1907 // keep notes in standard midi range
1908 clamp_to_0_127(new_pitch);
1910 // keep original pitch if note is dragged outside valid midi range
1911 if ((original_pitch != 0 && new_pitch == 0)
1912 || (original_pitch != 127 && new_pitch == 127)) {
1913 new_pitch = original_pitch;
1916 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
1917 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
1919 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
1924 // care about notes being moved beyond the upper/lower bounds on the canvas
1925 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
1926 highest_note_in_selection > midi_stream_view()->highest_note()) {
1927 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
1932 MidiRegionView::snap_pixel_to_frame(double x)
1934 PublicEditor& editor = trackview.editor();
1935 // x is region relative, convert it to global absolute frames
1936 nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
1937 editor.snap_to(frame);
1938 return frame - _region->position(); // convert back to region relative
1942 MidiRegionView::snap_frame_to_frame(nframes64_t x)
1944 PublicEditor& editor = trackview.editor();
1945 // x is region relative, convert it to global absolute frames
1946 nframes64_t frame = x + _region->position();
1947 editor.snap_to(frame);
1948 return frame - _region->position(); // convert back to region relative
1952 MidiRegionView::snap_to_pixel(double x)
1954 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
1958 MidiRegionView::get_position_pixels()
1960 nframes64_t region_frame = get_position();
1961 return trackview.editor().frame_to_pixel(region_frame);
1965 MidiRegionView::get_end_position_pixels()
1967 nframes64_t frame = get_position() + get_duration ();
1968 return trackview.editor().frame_to_pixel(frame);
1972 MidiRegionView::beats_to_frames(double beats) const
1974 return _time_converter.to(beats);
1978 MidiRegionView::frames_to_beats(nframes64_t frames) const
1980 return _time_converter.from(frames);
1984 MidiRegionView::begin_resizing (bool /*at_front*/)
1986 _resize_data.clear();
1988 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1989 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
1991 // only insert CanvasNotes into the map
1993 NoteResizeData *resize_data = new NoteResizeData();
1994 resize_data->canvas_note = note;
1996 // create a new SimpleRect from the note which will be the resize preview
1997 SimpleRect *resize_rect = new SimpleRect(
1998 *group, note->x1(), note->y1(), note->x2(), note->y2());
2000 // calculate the colors: get the color settings
2001 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2002 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2005 // make the resize preview notes more transparent and bright
2006 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2008 // calculate color based on note velocity
2009 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2010 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity()),
2014 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2015 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2017 resize_data->resize_rect = resize_rect;
2018 _resize_data.push_back(resize_data);
2024 MidiRegionView::update_resizing (bool at_front, double delta_x, bool relative)
2026 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2027 SimpleRect* resize_rect = (*i)->resize_rect;
2028 CanvasNote* canvas_note = (*i)->canvas_note;
2033 current_x = canvas_note->x1() + delta_x;
2035 // x is in track relative, transform it to region relative
2036 current_x = delta_x - get_position_pixels();
2040 current_x = canvas_note->x2() + delta_x;
2042 // x is in track relative, transform it to region relative
2043 current_x = delta_x - get_end_position_pixels ();
2048 resize_rect->property_x1() = snap_to_pixel(current_x);
2049 resize_rect->property_x2() = canvas_note->x2();
2051 resize_rect->property_x2() = snap_to_pixel(current_x);
2052 resize_rect->property_x1() = canvas_note->x1();
2058 MidiRegionView::commit_resizing (bool at_front, double delta_x, bool relative)
2060 start_diff_command(_("resize notes"));
2062 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2063 CanvasNote* canvas_note = (*i)->canvas_note;
2064 SimpleRect* resize_rect = (*i)->resize_rect;
2065 const double region_start = get_position_pixels();
2070 current_x = canvas_note->x1() + delta_x;
2072 // x is in track relative, transform it to region relative
2073 current_x = region_start + delta_x;
2077 current_x = canvas_note->x2() + delta_x;
2079 // x is in track relative, transform it to region relative
2080 current_x = region_start + delta_x;
2084 current_x = snap_pixel_to_frame (current_x);
2085 current_x = frames_to_beats (current_x);
2087 if (at_front && current_x < canvas_note->note()->end_time()) {
2088 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2090 double len = canvas_note->note()->time() - current_x;
2091 len += canvas_note->note()->length();
2094 /* XXX convert to beats */
2095 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2100 double len = current_x - canvas_note->note()->time();
2103 /* XXX convert to beats */
2104 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2112 _resize_data.clear();
2117 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2119 uint8_t new_velocity;
2122 new_velocity = event->note()->velocity() + velocity;
2123 clamp_to_0_127(new_velocity);
2125 new_velocity = velocity;
2128 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2132 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2137 new_note = event->note()->note() + note;
2142 clamp_to_0_127 (new_note);
2143 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2147 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2149 bool change_start = false;
2150 bool change_length = false;
2151 Evoral::MusicalTime new_start;
2152 Evoral::MusicalTime new_length;
2154 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2156 front_delta: if positive - move the start of the note later in time (shortening it)
2157 if negative - move the start of the note earlier in time (lengthening it)
2159 end_delta: if positive - move the end of the note later in time (lengthening it)
2160 if negative - move the end of the note earlier in time (shortening it)
2164 if (front_delta < 0) {
2166 if (event->note()->time() < -front_delta) {
2169 new_start = event->note()->time() + front_delta; // moves earlier
2172 /* start moved toward zero, so move the end point out to where it used to be.
2173 Note that front_delta is negative, so this increases the length.
2176 new_length = event->note()->length() - front_delta;
2177 change_start = true;
2178 change_length = true;
2182 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2184 if (new_pos < event->note()->end_time()) {
2185 new_start = event->note()->time() + front_delta;
2186 /* start moved toward the end, so move the end point back to where it used to be */
2187 new_length = event->note()->length() - front_delta;
2188 change_start = true;
2189 change_length = true;
2196 bool can_change = true;
2197 if (end_delta < 0) {
2198 if (event->note()->length() < -end_delta) {
2204 new_length = event->note()->length() + end_delta;
2205 change_length = true;
2210 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2213 if (change_length) {
2214 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2219 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2221 Evoral::MusicalTime new_time;
2225 if (event->note()->time() < -delta) {
2228 new_time = event->note()->time() + delta;
2231 new_time = event->note()->time() + delta;
2237 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2241 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2245 if (_selection.empty()) {
2260 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2261 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2267 start_diff_command(_("change velocities"));
2269 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2270 Selection::iterator next = i;
2272 change_note_velocity (*i, delta, true);
2281 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2283 if (_selection.empty()) {
2300 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2302 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2306 if ((int8_t) (*i)->note()->note() + delta > 127) {
2313 start_diff_command (_("transpose"));
2315 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2316 Selection::iterator next = i;
2318 change_note_note (*i, delta, true);
2326 MidiRegionView::change_note_lengths (bool fine, bool shorter, bool start, bool end)
2328 Evoral::MusicalTime delta;
2333 /* grab the current grid distance */
2335 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2337 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2338 cerr << "Grid type not available as beats - TO BE FIXED\n";
2347 start_diff_command (_("change note lengths"));
2349 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2350 Selection::iterator next = i;
2353 /* note the negation of the delta for start */
2355 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2364 MidiRegionView::nudge_notes (bool forward)
2366 if (_selection.empty()) {
2370 /* pick a note as the point along the timeline to get the nudge distance.
2371 its not necessarily the earliest note, so we may want to pull the notes out
2372 into a vector and sort before using the first one.
2375 nframes64_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2377 nframes64_t distance;
2379 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2381 /* grid is off - use nudge distance */
2383 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2389 nframes64_t next_pos = ref_point;
2392 /* XXX need check on max_frames, but that needs max_frames64 or something */
2395 if (next_pos == 0) {
2401 cerr << "ref point was " << ref_point << " next was " << next_pos;
2402 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2403 distance = ref_point - next_pos;
2404 cerr << " final is " << next_pos << " distance = " << distance << endl;
2407 if (distance == 0) {
2411 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2417 start_diff_command (_("nudge"));
2419 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2420 Selection::iterator next = i;
2422 change_note_time (*i, delta, true);
2430 MidiRegionView::change_channel(uint8_t channel)
2432 start_diff_command(_("change channel"));
2433 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2434 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2441 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2443 if (_mouse_state == SelectTouchDragging) {
2444 note_selected(ev, true);
2447 show_verbose_canvas_cursor (ev->note ());
2451 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2453 PublicEditor& editor (trackview.editor());
2454 editor.hide_verbose_canvas_cursor ();
2459 MidiRegionView::switch_source(boost::shared_ptr<Source> src)
2461 boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(src);
2463 display_model(msrc->model());
2467 MidiRegionView::set_frame_color()
2470 if (_selected && should_show_selection) {
2471 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2473 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2479 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2483 case FilterChannels:
2484 _force_channel = -1;
2487 _force_channel = mask;
2488 mask = 0xFFFF; // Show all notes as active (below)
2491 // Update notes for selection
2492 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2493 (*i)->on_channel_selection_change(mask);
2496 _last_channel_selection = mask;
2500 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2502 _model_name = model;
2503 _custom_device_mode = custom_device_mode;
2508 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2510 if (_selection.empty()) {
2514 PublicEditor& editor (trackview.editor());
2519 editor.get_cut_buffer().add (selection_as_cut_buffer());
2527 start_delta_command();
2529 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2535 delta_remove_note (*i);
2545 MidiRegionView::selection_as_cut_buffer () const
2549 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2550 NoteType* n = (*i)->note().get();
2551 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2554 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2561 MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
2567 start_delta_command (_("paste"));
2569 Evoral::MusicalTime beat_delta;
2570 Evoral::MusicalTime paste_pos_beats;
2571 Evoral::MusicalTime duration;
2572 Evoral::MusicalTime end_point;
2574 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2575 paste_pos_beats = frames_to_beats (pos - _region->position());
2576 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2577 paste_pos_beats = 0;
2579 _selection.clear ();
2581 for (int n = 0; n < (int) times; ++n) {
2583 cerr << "Pasting " << mcb.notes().size() << " for the " << n+1 << "th time\n";
2585 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2587 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2588 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2590 /* make all newly added notes selected */
2592 delta_add_note (copied_note, true);
2593 end_point = copied_note->end_time();
2596 paste_pos_beats += duration;
2599 /* if we pasted past the current end of the region, extend the region */
2601 nframes64_t end_frame = _region->position() + beats_to_frames (end_point);
2602 nframes64_t region_end = _region->position() + _region->length() - 1;
2604 if (end_frame > region_end) {
2606 cerr << "region end is now " << end_frame << " to extend from " << region_end << endl;
2608 trackview.session()->begin_reversible_command (_("paste"));
2610 _region->clear_history ();
2611 _region->set_length (end_frame, this);
2612 trackview.session()->add_command (new StatefulDiffCommand (_region));
2615 cerr << "region end finally at " << _region->position() + _region->length() - 1;
2619 struct EventNoteTimeEarlyFirstComparator {
2620 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2621 return a->note()->time() < b->note()->time();
2626 MidiRegionView::time_sort_events ()
2628 if (!_sort_needed) {
2632 EventNoteTimeEarlyFirstComparator cmp;
2635 _sort_needed = false;
2639 MidiRegionView::goto_next_note ()
2641 // nframes64_t pos = -1;
2642 bool use_next = false;
2644 if (_events.back()->selected()) {
2648 time_sort_events ();
2650 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2651 if ((*i)->selected()) {
2654 } else if (use_next) {
2656 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2661 /* use the first one */
2663 unique_select (_events.front());
2668 MidiRegionView::goto_previous_note ()
2670 // nframes64_t pos = -1;
2671 bool use_next = false;
2673 if (_events.front()->selected()) {
2677 time_sort_events ();
2679 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2680 if ((*i)->selected()) {
2683 } else if (use_next) {
2685 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2690 /* use the last one */
2692 unique_select (*(_events.rbegin()));
2696 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2698 bool had_selected = false;
2700 time_sort_events ();
2702 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2703 if ((*i)->selected()) {
2704 selected.insert ((*i)->note());
2705 had_selected = true;
2709 if (allow_all_if_none_selected && !had_selected) {
2710 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2711 selected.insert ((*i)->note());
2717 MidiRegionView::update_ghost_note (double x, double y)
2723 nframes64_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
2724 trackview.editor().snap_to (f);
2725 f -= _region->position ();
2728 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
2733 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
2735 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
2736 _ghost_note->note()->set_length (length);
2737 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
2739 update_note (_ghost_note);
2741 show_verbose_canvas_cursor (_ghost_note->note ());
2745 MidiRegionView::create_ghost_note (double x, double y)
2750 boost::shared_ptr<NoteType> g (new NoteType);
2751 _ghost_note = new NoEventCanvasNote (*this, *group, g);
2752 update_ghost_note (x, y);
2753 _ghost_note->show ();
2758 show_verbose_canvas_cursor (_ghost_note->note ());
2762 MidiRegionView::snap_changed ()
2768 create_ghost_note (_last_ghost_x, _last_ghost_y);
2772 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
2775 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (n->note()).c_str(), (int) n->note ());
2776 trackview.editor().show_verbose_canvas_cursor_with (buf);