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"
33 #include "ardour/playlist.h"
34 #include "ardour/tempo.h"
35 #include "ardour/midi_region.h"
36 #include "ardour/midi_source.h"
37 #include "ardour/midi_diskstream.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"
45 #include "automation_region_view.h"
46 #include "automation_time_axis.h"
47 #include "canvas-hit.h"
48 #include "canvas-note.h"
49 #include "canvas-program-change.h"
50 #include "ghostregion.h"
51 #include "gui_thread.h"
53 #include "midi_cut_buffer.h"
54 #include "midi_list_editor.h"
55 #include "midi_region_view.h"
56 #include "midi_streamview.h"
57 #include "midi_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "midi_util.h"
60 #include "public_editor.h"
61 #include "selection.h"
62 #include "simpleline.h"
63 #include "streamview.h"
68 using namespace ARDOUR;
70 using namespace Editing;
71 using namespace ArdourCanvas;
72 using Gtkmm2ext::Keyboard;
74 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
75 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
76 : RegionView (parent, tv, r, spu, basic_color)
78 , _last_channel_selection(0xFFFF)
79 , _default_note_length(1.0)
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))
91 , _optimization_iterator (_events.end())
93 _note_group->raise_to_top();
96 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
97 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
98 TimeAxisViewItem::Visibility visibility)
99 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
101 , _last_channel_selection(0xFFFF)
102 , _default_note_length(1.0)
103 , _model_name(string())
104 , _custom_device_mode(string())
106 , _note_group(new ArdourCanvas::Group(*parent))
111 , _sort_needed (true)
112 , _optimization_iterator (_events.end())
115 _note_group->raise_to_top();
119 MidiRegionView::MidiRegionView (const MidiRegionView& other)
120 : sigc::trackable(other)
123 , _last_channel_selection(0xFFFF)
124 , _default_note_length(1.0)
125 , _model_name(string())
126 , _custom_device_mode(string())
128 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
133 , _sort_needed (true)
134 , _optimization_iterator (_events.end())
139 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
140 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
145 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
146 : RegionView (other, boost::shared_ptr<Region> (region))
148 , _last_channel_selection(0xFFFF)
149 , _default_note_length(1.0)
150 , _model_name(string())
151 , _custom_device_mode(string())
153 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
158 , _sort_needed (true)
159 , _optimization_iterator (_events.end())
164 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
165 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
171 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
174 midi_region()->midi_source(0)->load_model();
177 _model = midi_region()->midi_source(0)->model();
178 _enable_display = false;
180 RegionView::init (basic_color, false);
182 compute_colors (basic_color);
184 set_height (trackview.current_height());
187 region_sync_changed ();
188 region_resized (BoundsChanged);
191 reset_width_dependent_items (_pixel_width);
195 _enable_display = true;
198 display_model (_model);
202 group->raise_to_top();
203 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
205 midi_view()->signal_channel_mode_changed().connect(
206 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
208 midi_view()->signal_midi_patch_settings_changed().connect(
209 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
213 MidiRegionView::canvas_event(GdkEvent* ev)
215 PublicEditor& editor (trackview.editor());
217 if (!editor.internal_editing()) {
221 static double drag_start_x, drag_start_y;
222 static double last_x, last_y;
223 double event_x, event_y;
224 nframes64_t event_frame = 0;
227 static ArdourCanvas::SimpleRect* drag_rect = 0;
229 /* XXX: note that as of August 2009, the GnomeCanvas does not propagate scroll events
230 to its items, which means that ev->type == GDK_SCROLL will never be seen
235 fine = Keyboard::modifier_state_equals (ev->scroll.state, Keyboard::Level4Modifier);
237 if (ev->scroll.direction == GDK_SCROLL_UP) {
238 change_velocities (true, fine, false);
240 } else if (ev->scroll.direction == GDK_SCROLL_DOWN) {
241 change_velocities (false, fine, false);
250 /* since GTK bindings are generally activated on press, and since
251 detectable auto-repeat is the name of the game and only sends
252 repeated presses, carry out key actions at key press, not release.
255 if (ev->key.keyval == GDK_Alt_L || ev->key.keyval == GDK_Alt_R){
256 _mouse_state = SelectTouchDragging;
259 } else if (ev->key.keyval == GDK_Escape) {
263 } else if (ev->key.keyval == GDK_comma || ev->key.keyval == GDK_period) {
265 bool start = (ev->key.keyval == GDK_comma);
266 bool end = (ev->key.keyval == GDK_period);
267 bool shorter = Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier);
268 fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier);
270 change_note_lengths (fine, shorter, start, end);
274 } else if (ev->key.keyval == GDK_Delete) {
279 } else if (ev->key.keyval == GDK_Tab) {
281 if (Keyboard::modifier_state_equals (ev->key.state, Keyboard::PrimaryModifier)) {
282 goto_previous_note ();
288 } else if (ev->key.keyval == GDK_Up) {
290 bool allow_smush = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier);
291 bool fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::TertiaryModifier);
293 if (Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier)) {
294 change_velocities (true, fine, allow_smush);
296 transpose (true, fine, allow_smush);
300 } else if (ev->key.keyval == GDK_Down) {
302 bool allow_smush = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier);
303 fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::TertiaryModifier);
305 if (Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier)) {
306 change_velocities (false, fine, allow_smush);
308 transpose (false, fine, allow_smush);
312 } else if (ev->key.keyval == GDK_Left) {
317 } else if (ev->key.keyval == GDK_Right) {
322 } else if (ev->key.keyval == GDK_Control_L) {
325 } else if (ev->key.keyval == GDK_r) {
326 /* if we're not step editing, this really doesn't matter */
327 midi_view()->step_edit_rest ();
333 case GDK_KEY_RELEASE:
334 if (ev->key.keyval == GDK_Alt_L || ev->key.keyval == GDK_Alt_R) {
340 case GDK_BUTTON_PRESS:
341 if (_mouse_state != SelectTouchDragging && ev->button.button == 1) {
342 _pressed_button = ev->button.button;
343 _mouse_state = Pressed;
346 _pressed_button = ev->button.button;
349 case GDK_2BUTTON_PRESS:
352 case GDK_ENTER_NOTIFY:
353 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
354 Keyboard::magic_widget_grab_focus();
358 case GDK_MOTION_NOTIFY:
359 event_x = ev->motion.x;
360 event_y = ev->motion.y;
361 group->w2i(event_x, event_y);
363 // convert event_x to global frame
364 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
365 trackview.editor().snap_to(event_frame);
366 // convert event_frame back to local coordinates relative to position
367 event_frame -= _region->position();
369 switch (_mouse_state) {
370 case Pressed: // Drag start
373 if (_pressed_button == 1 && editor.current_mouse_mode() == MouseObject) {
374 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
375 Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
378 drag_start_x = event_x;
379 drag_start_y = event_y;
381 drag_rect = new ArdourCanvas::SimpleRect(*group);
382 drag_rect->property_x1() = event_x;
383 drag_rect->property_y1() = event_y;
384 drag_rect->property_x2() = event_x;
385 drag_rect->property_y2() = event_y;
386 drag_rect->property_outline_what() = 0xFF;
387 drag_rect->property_outline_color_rgba()
388 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
389 drag_rect->property_fill_color_rgba()
390 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
392 _mouse_state = SelectRectDragging;
395 // Add note drag start
396 } else if (editor.current_mouse_mode() == MouseRange) {
397 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
398 Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
401 drag_start_x = event_x;
402 drag_start_y = event_y;
404 drag_rect = new ArdourCanvas::SimpleRect(*group);
405 drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
407 drag_rect->property_y1() = midi_stream_view()->note_to_y(
408 midi_stream_view()->y_to_note(event_y));
409 drag_rect->property_x2() = event_x;
410 drag_rect->property_y2() = drag_rect->property_y1()
411 + floor(midi_stream_view()->note_height());
412 drag_rect->property_outline_what() = 0xFF;
413 drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
414 drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
416 _mouse_state = AddDragging;
422 case SelectRectDragging: // Select drag motion
423 case AddDragging: // Add note drag motion
424 if (ev->motion.is_hint) {
427 GdkModifierType state;
428 gdk_window_get_pointer(ev->motion.window, &t_x, &t_y, &state);
433 if (_mouse_state == AddDragging)
434 event_x = trackview.editor().frame_to_pixel(event_frame);
437 if (event_x > drag_start_x)
438 drag_rect->property_x2() = event_x;
440 drag_rect->property_x1() = event_x;
443 if (drag_rect && _mouse_state == SelectRectDragging) {
444 if (event_y > drag_start_y)
445 drag_rect->property_y2() = event_y;
447 drag_rect->property_y1() = event_y;
449 update_drag_selection(drag_start_x, event_x, drag_start_y, event_y);
455 case SelectTouchDragging:
463 case GDK_BUTTON_RELEASE:
464 event_x = ev->motion.x;
465 event_y = ev->motion.y;
466 group->w2i(event_x, event_y);
467 group->ungrab(ev->button.time);
468 event_frame = trackview.editor().pixel_to_frame(event_x);
470 if (ev->button.button == 3) {
472 } else if (_pressed_button != 1) {
476 switch (_mouse_state) {
477 case Pressed: // Clicked
478 switch (editor.current_mouse_mode()) {
484 create_note_at(event_x, event_y, _default_note_length);
491 case SelectRectDragging: // Select drag done
496 case AddDragging: // Add drag done
498 if (drag_rect->property_x2() > drag_rect->property_x1() + 2) {
499 const double x = drag_rect->property_x1();
500 const double length = trackview.editor().pixel_to_frame(
501 drag_rect->property_x2() - drag_rect->property_x1());
503 create_note_at(x, drag_rect->property_y1(), frames_to_beats(length));
518 MidiRegionView::show_list_editor ()
520 MidiListEditor* mle = new MidiListEditor (trackview.session(), midi_region());
524 /** Add a note to the model, and the view, at a canvas (click) coordinate.
525 * \param x horizontal position in pixels
526 * \param y vertical position in pixels
527 * \param length duration of the note in beats */
529 MidiRegionView::create_note_at(double x, double y, double length)
531 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
532 MidiStreamView* const view = mtv->midi_view();
534 double note = midi_stream_view()->y_to_note(y);
537 assert(note <= 127.0);
539 // Start of note in frames relative to region start
540 nframes64_t start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
541 assert(start_frames >= 0);
544 length = frames_to_beats(
545 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
547 const boost::shared_ptr<NoteType> new_note(new NoteType(0,
548 frames_to_beats(start_frames + _region->start()), length,
549 (uint8_t)note, 0x40));
551 view->update_note_range(new_note->note());
553 MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
555 _model->apply_command(trackview.session(), cmd);
557 play_midi_note (new_note);
561 MidiRegionView::clear_events()
566 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
567 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
572 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
577 _pgm_changes.clear();
579 _optimization_iterator = _events.end();
584 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
587 content_connection.disconnect ();
588 content_connection = _model->ContentsChanged.connect(
589 sigc::mem_fun(this, &MidiRegionView::redisplay_model));
592 if (_enable_display) {
599 MidiRegionView::start_delta_command(string name)
601 if (!_delta_command) {
602 _delta_command = _model->new_delta_command(name);
607 MidiRegionView::start_diff_command(string name)
609 if (!_diff_command) {
610 _diff_command = _model->new_diff_command(name);
615 MidiRegionView::delta_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
617 if (_delta_command) {
618 _delta_command->add(note);
621 _marked_for_selection.insert(note);
624 _marked_for_velocity.insert(note);
629 MidiRegionView::delta_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
631 if (_delta_command && ev->note()) {
632 _delta_command->remove(ev->note());
637 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
638 MidiModel::DiffCommand::Property property,
642 _diff_command->change (ev->note(), property, val);
647 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
648 MidiModel::DiffCommand::Property property,
649 Evoral::MusicalTime val)
652 _diff_command->change (ev->note(), property, val);
657 MidiRegionView::apply_delta()
659 if (!_delta_command) {
663 // Mark all selected notes for selection when model reloads
664 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
665 _marked_for_selection.insert((*i)->note());
668 _model->apply_command(trackview.session(), _delta_command);
670 midi_view()->midi_track()->diskstream()->playlist_modified();
672 _marked_for_selection.clear();
673 _marked_for_velocity.clear();
677 MidiRegionView::apply_diff ()
679 if (!_diff_command) {
683 _model->apply_command(trackview.session(), _diff_command);
685 midi_view()->midi_track()->diskstream()->playlist_modified();
687 _marked_for_velocity.clear();
691 MidiRegionView::apply_delta_as_subcommand()
693 if (!_delta_command) {
697 // Mark all selected notes for selection when model reloads
698 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
699 _marked_for_selection.insert((*i)->note());
702 _model->apply_command_as_subcommand(trackview.session(), _delta_command);
704 midi_view()->midi_track()->diskstream()->playlist_modified();
706 _marked_for_selection.clear();
707 _marked_for_velocity.clear();
711 MidiRegionView::apply_diff_as_subcommand()
713 if (!_diff_command) {
717 // Mark all selected notes for selection when model reloads
718 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
719 _marked_for_selection.insert((*i)->note());
722 _model->apply_command_as_subcommand(trackview.session(), _diff_command);
724 midi_view()->midi_track()->diskstream()->playlist_modified();
726 _marked_for_selection.clear();
727 _marked_for_velocity.clear();
731 MidiRegionView::abort_command()
733 delete _delta_command;
735 delete _diff_command;
741 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
743 if (_optimization_iterator != _events.end()) {
744 ++_optimization_iterator;
747 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
748 return *_optimization_iterator;
751 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
752 if ((*_optimization_iterator)->note() == note) {
753 return *_optimization_iterator;
761 MidiRegionView::redisplay_model()
763 // Don't redisplay the model if we're currently recording and displaying that
769 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
773 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
777 MidiModel::ReadLock lock(_model->read_lock());
779 MidiModel::Notes& notes (_model->notes());
780 _optimization_iterator = _events.begin();
782 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
784 boost::shared_ptr<NoteType> note (*n);
785 CanvasNoteEvent* cne;
788 if (note_in_region_range (note, visible)) {
790 if ((cne = find_canvas_note (note)) != 0) {
797 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
799 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
811 add_note (note, visible);
816 if ((cne = find_canvas_note (note)) != 0) {
823 /* remove note items that are no longer valid */
825 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
826 if (!(*i)->valid ()) {
828 i = _events.erase (i);
835 display_program_changes();
837 _marked_for_selection.clear ();
838 _marked_for_velocity.clear ();
840 /* we may have caused _events to contain things out of order (e.g. if a note
841 moved earlier or later). we don't generally need them in time order, but
842 make a note that a sort is required for those cases that require it.
849 MidiRegionView::display_program_changes()
851 boost::shared_ptr<Evoral::Control> control = _model->control(MidiPgmChangeAutomation);
856 Glib::Mutex::Lock lock (control->list()->lock());
858 uint8_t channel = control->parameter().channel();
860 for (AutomationList::const_iterator event = control->list()->begin();
861 event != control->list()->end(); ++event) {
862 double event_time = (*event)->when;
863 double program_number = floor((*event)->value + 0.5);
865 // Get current value of bank select MSB at time of the program change
866 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
867 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
869 if (msb_control != 0) {
870 msb = uint8_t(floor(msb_control->get_float(true, event_time) + 0.5));
873 // Get current value of bank select LSB at time of the program change
874 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
875 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
877 if (lsb_control != 0) {
878 lsb = uint8_t(floor(lsb_control->get_float(true, event_time) + 0.5));
881 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
883 boost::shared_ptr<MIDI::Name::Patch> patch =
884 MIDI::Name::MidiPatchManager::instance().find_patch(
885 _model_name, _custom_device_mode, channel, patch_key);
887 PCEvent program_change(event_time, uint8_t(program_number), channel);
890 add_pgm_change(program_change, patch->name());
893 snprintf(buf, 4, "%d", int(program_number));
894 add_pgm_change(program_change, buf);
900 MidiRegionView::display_sysexes()
902 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
903 Evoral::MusicalTime time = (*i)->time();
908 for (uint32_t b = 0; b < (*i)->size(); ++b) {
909 str << int((*i)->buffer()[b]);
910 if (b != (*i)->size() -1) {
914 string text = str.str();
916 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
918 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
920 double height = midi_stream_view()->contents_height();
922 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
923 new CanvasSysEx(*this, *group, text, height, x, 1.0));
925 // Show unless program change is beyond the region bounds
926 if (time - _region->start() >= _region->length() || time < _region->start()) {
932 _sys_exes.push_back(sysex);
937 MidiRegionView::~MidiRegionView ()
939 in_destructor = true;
941 RegionViewGoingAway (this); /* EMIT_SIGNAL */
950 delete _delta_command;
954 MidiRegionView::region_resized (Change what_changed)
956 RegionView::region_resized(what_changed);
958 if (what_changed & ARDOUR::PositionChanged) {
959 set_duration(_region->length(), 0);
960 if (_enable_display) {
967 MidiRegionView::reset_width_dependent_items (double pixel_width)
969 RegionView::reset_width_dependent_items(pixel_width);
970 assert(_pixel_width == pixel_width);
972 if (_enable_display) {
978 MidiRegionView::set_height (double height)
980 static const double FUDGE = 2.0;
981 const double old_height = _height;
982 RegionView::set_height(height);
983 _height = height - FUDGE;
985 apply_note_range(midi_stream_view()->lowest_note(),
986 midi_stream_view()->highest_note(),
987 height != old_height + FUDGE);
990 name_pixbuf->raise_to_top();
995 /** Apply the current note range from the stream view
996 * by repositioning/hiding notes as necessary
999 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1001 if (!_enable_display) {
1005 if (!force && _current_range_min == min && _current_range_max == max) {
1009 _current_range_min = min;
1010 _current_range_max = max;
1012 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1013 CanvasNoteEvent* event = *i;
1014 boost::shared_ptr<NoteType> note (event->note());
1016 if (note->note() < _current_range_min ||
1017 note->note() > _current_range_max) {
1023 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1025 const double y1 = midi_stream_view()->note_to_y(note->note());
1026 const double y2 = y1 + floor(midi_stream_view()->note_height());
1028 cnote->property_y1() = y1;
1029 cnote->property_y2() = y2;
1031 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1033 double x = trackview.editor().frame_to_pixel(
1034 beats_to_frames(note->time()) - _region->start());
1035 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1036 double y = midi_stream_view()->note_to_y(event->note()->note())
1037 + ((diamond_size-2.0) / 4.0);
1039 chit->set_height (diamond_size);
1040 chit->move (x - chit->x1(), y - chit->y1());
1047 MidiRegionView::add_ghost (TimeAxisView& tv)
1051 double unit_position = _region->position () / samples_per_unit;
1052 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1053 MidiGhostRegion* ghost;
1055 if (mtv && mtv->midi_view()) {
1056 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1057 to allow having midi notes on top of note lines and waveforms.
1059 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1061 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1064 ghost->set_height ();
1065 ghost->set_duration (_region->length() / samples_per_unit);
1066 ghosts.push_back (ghost);
1068 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1069 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1070 ghost->add_note(note);
1074 ghost->GoingAway.connect (sigc::mem_fun(*this, &MidiRegionView::remove_ghost));
1080 /** Begin tracking note state for successive calls to add_event
1083 MidiRegionView::begin_write()
1085 assert(!_active_notes);
1086 _active_notes = new CanvasNote*[128];
1087 for (unsigned i=0; i < 128; ++i) {
1088 _active_notes[i] = 0;
1093 /** Destroy note state for add_event
1096 MidiRegionView::end_write()
1098 delete[] _active_notes;
1100 _marked_for_selection.clear();
1101 _marked_for_velocity.clear();
1105 /** Resolve an active MIDI note (while recording).
1108 MidiRegionView::resolve_note(uint8_t note, double end_time)
1110 if (midi_view()->note_mode() != Sustained) {
1114 if (_active_notes && _active_notes[note]) {
1115 const nframes64_t end_time_frames = beats_to_frames(end_time);
1116 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1117 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1118 _active_notes[note] = 0;
1123 /** Extend active notes to rightmost edge of region (if length is changed)
1126 MidiRegionView::extend_active_notes()
1128 if (!_active_notes) {
1132 for (unsigned i=0; i < 128; ++i) {
1133 if (_active_notes[i]) {
1134 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1140 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1142 if (!trackview.editor().sound_notes()) {
1146 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1149 route_ui->midi_track()->write_immediate_event(
1150 note->on_event().size(), note->on_event().buffer());
1152 const double note_length_beats = (note->off_event().time() - note->on_event().time());
1153 nframes_t note_length_ms = beats_to_frames(note_length_beats)
1154 * (1000 / (double)route_ui->session().nominal_frame_rate());
1155 Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(this, &MidiRegionView::play_midi_note_off), note),
1156 note_length_ms, G_PRIORITY_DEFAULT);
1160 MidiRegionView::play_midi_note_off(boost::shared_ptr<NoteType> note)
1162 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1165 route_ui->midi_track()->write_immediate_event(
1166 note->off_event().size(), note->off_event().buffer());
1172 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1174 const nframes64_t note_start_frames = beats_to_frames(note->time());
1176 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1177 (note_start_frames < _region->start());
1179 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1180 (note->note() <= midi_stream_view()->highest_note());
1186 MidiRegionView::update_note (CanvasNote* ev)
1188 boost::shared_ptr<NoteType> note = ev->note();
1190 const nframes64_t note_start_frames = beats_to_frames(note->time());
1191 const nframes64_t note_end_frames = beats_to_frames(note->end_time());
1193 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1194 const double y1 = midi_stream_view()->note_to_y(note->note());
1195 const double note_endpixel =
1196 trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1198 ev->property_x1() = x;
1199 ev->property_y1() = y1;
1200 if (note->length() > 0) {
1201 ev->property_x2() = note_endpixel;
1203 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1205 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1207 if (note->length() == 0) {
1208 if (_active_notes) {
1209 assert(note->note() < 128);
1210 // If this note is already active there's a stuck note,
1211 // finish the old note rectangle
1212 if (_active_notes[note->note()]) {
1213 CanvasNote* const old_rect = _active_notes[note->note()];
1214 boost::shared_ptr<NoteType> old_note = old_rect->note();
1215 old_rect->property_x2() = x;
1216 old_rect->property_outline_what() = (guint32) 0xF;
1218 _active_notes[note->note()] = ev;
1220 /* outline all but right edge */
1221 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1223 /* outline all edges */
1224 ev->property_outline_what() = (guint32) 0xF;
1229 MidiRegionView::update_hit (CanvasHit* ev)
1231 boost::shared_ptr<NoteType> note = ev->note();
1233 const nframes64_t note_start_frames = beats_to_frames(note->time());
1234 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1235 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1236 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1241 /** Add a MIDI note to the view (with length).
1243 * If in sustained mode, notes with length 0 will be considered active
1244 * notes, and resolve_note should be called when the corresponding note off
1245 * event arrives, to properly display the note.
1248 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1250 CanvasNoteEvent* event = 0;
1252 assert(note->time() >= 0);
1253 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1255 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1257 if (midi_view()->note_mode() == Sustained) {
1259 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1261 update_note (ev_rect);
1265 MidiGhostRegion* gr;
1267 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1268 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1269 gr->add_note(ev_rect);
1273 } else if (midi_view()->note_mode() == Percussive) {
1275 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1277 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1279 update_hit (ev_diamond);
1288 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1289 note_selected(event, true);
1292 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1293 event->show_velocity();
1295 event->on_channel_selection_change(_last_channel_selection);
1296 _events.push_back(event);
1307 MidiRegionView::add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1308 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1310 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1312 start_delta_command (_("step add"));
1313 delta_add_note (new_note, true, false);
1316 /* potentially extend region to hold new note */
1318 nframes64_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1319 nframes64_t region_end = _region->position() + _region->length() - 1;
1321 if (end_frame > region_end) {
1322 _region->set_length (end_frame, this);
1329 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1331 assert(program.time >= 0);
1333 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1334 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1336 double height = midi_stream_view()->contents_height();
1338 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1339 new CanvasProgramChange(*this, *group,
1344 _custom_device_mode,
1345 program.time, program.channel, program.value));
1347 // Show unless program change is beyond the region bounds
1348 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1354 _pgm_changes.push_back(pgm_change);
1358 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1360 cerr << "getting patch key at " << time << " for channel " << channel << endl;
1361 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1362 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1364 if (msb_control != 0) {
1365 msb = int(msb_control->get_float(true, time));
1366 cerr << "got msb " << msb;
1369 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1370 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1372 if (lsb_control != 0) {
1373 lsb = lsb_control->get_float(true, time);
1374 cerr << " got lsb " << lsb;
1377 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1378 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1379 float program_number = -1.0;
1380 if (program_control != 0) {
1381 program_number = program_control->get_float(true, time);
1382 cerr << " got program " << program_number << endl;
1385 key.msb = (int) floor(msb + 0.5);
1386 key.lsb = (int) floor(lsb + 0.5);
1387 key.program_number = (int) floor(program_number + 0.5);
1388 assert(key.is_sane());
1393 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1395 // TODO: Get the real event here and alter them at the original times
1396 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1397 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1398 if (msb_control != 0) {
1399 msb_control->set_float(float(new_patch.msb), true, old_program.time);
1402 // TODO: Get the real event here and alter them at the original times
1403 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1404 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1405 if (lsb_control != 0) {
1406 lsb_control->set_float(float(new_patch.lsb), true, old_program.time);
1409 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1410 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1412 assert(program_control != 0);
1413 program_control->set_float(float(new_patch.program_number), true, old_program.time);
1419 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1421 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1422 alter_program_change(program_change_event, new_patch);
1426 MidiRegionView::previous_program(CanvasProgramChange& program)
1428 MIDI::Name::PatchPrimaryKey key;
1429 get_patch_key_at(program.event_time(), program.channel(), key);
1431 boost::shared_ptr<MIDI::Name::Patch> patch =
1432 MIDI::Name::MidiPatchManager::instance().previous_patch(
1434 _custom_device_mode,
1438 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1440 alter_program_change(program_change_event, patch->patch_primary_key());
1445 MidiRegionView::next_program(CanvasProgramChange& program)
1447 MIDI::Name::PatchPrimaryKey key;
1448 get_patch_key_at(program.event_time(), program.channel(), key);
1450 boost::shared_ptr<MIDI::Name::Patch> patch =
1451 MIDI::Name::MidiPatchManager::instance().next_patch(
1453 _custom_device_mode,
1457 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1459 alter_program_change(program_change_event, patch->patch_primary_key());
1464 MidiRegionView::delete_selection()
1466 if (_selection.empty()) {
1470 start_delta_command (_("delete selection"));
1472 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1473 if ((*i)->selected()) {
1474 _delta_command->remove((*i)->note());
1484 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1486 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1487 if ((*i)->selected() && (*i) != ev) {
1488 (*i)->selected(false);
1489 (*i)->hide_velocity();
1497 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1499 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1502 Selection::iterator tmp = i;
1505 (*i)->selected (false);
1506 _selection.erase (i);
1515 /* don't bother with removing this regionview from the editor selection,
1516 since we're about to add another note, and thus put/keep this
1517 regionview in the editor selection.
1520 if (!ev->selected()) {
1521 add_to_selection (ev);
1526 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1529 clear_selection_except(ev);
1534 if (!ev->selected()) {
1535 add_to_selection (ev);
1539 /* find end of latest note selected, select all between that and the start of "ev" */
1541 Evoral::MusicalTime earliest = DBL_MAX;
1542 Evoral::MusicalTime latest = 0;
1544 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1545 if ((*i)->note()->end_time() > latest) {
1546 latest = (*i)->note()->end_time();
1548 if ((*i)->note()->time() < earliest) {
1549 earliest = (*i)->note()->time();
1553 if (ev->note()->end_time() > latest) {
1554 latest = ev->note()->end_time();
1557 if (ev->note()->time() < earliest) {
1558 earliest = ev->note()->time();
1561 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1563 /* find notes entirely within OR spanning the earliest..latest range */
1565 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1566 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1567 add_to_selection (*i);
1571 /* if events were guaranteed to be time sorted, we could do this.
1572 but as of sept 10th 2009, they no longer are.
1575 if ((*i)->note()->time() > latest) {
1584 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1586 remove_from_selection (ev);
1590 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1600 // TODO: Make this faster by storing the last updated selection rect, and only
1601 // adjusting things that are in the area that appears/disappeared.
1602 // We probably need a tree to be able to find events in O(log(n)) time.
1604 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1606 /* check if any corner of the note is inside the rect
1609 1) this is computing "touched by", not "contained by" the rect.
1610 2) this does not require that events be sorted in time.
1613 const double ix1 = (*i)->x1();
1614 const double ix2 = (*i)->x2();
1615 const double iy1 = (*i)->y1();
1616 const double iy2 = (*i)->y2();
1618 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1619 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1620 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1621 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1624 if (!(*i)->selected()) {
1625 add_to_selection (*i);
1627 } else if ((*i)->selected()) {
1628 // Not inside rectangle
1629 remove_from_selection (*i);
1635 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1637 Selection::iterator i = _selection.find (ev);
1639 if (i != _selection.end()) {
1640 _selection.erase (i);
1643 ev->selected (false);
1644 ev->hide_velocity ();
1646 if (_selection.empty()) {
1647 PublicEditor& editor (trackview.editor());
1648 editor.get_selection().remove (this);
1653 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
1655 bool add_mrv_selection = false;
1657 if (_selection.empty()) {
1658 add_mrv_selection = true;
1661 if (_selection.insert (ev).second) {
1662 ev->selected (true);
1663 play_midi_note ((ev)->note());
1666 if (add_mrv_selection) {
1667 PublicEditor& editor (trackview.editor());
1668 editor.get_selection().add (this);
1673 MidiRegionView::move_selection(double dx, double dy)
1675 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1676 (*i)->move_event(dx, dy);
1681 MidiRegionView::note_dropped(CanvasNoteEvent *, double dt, int8_t dnote)
1683 assert (!_selection.empty());
1685 uint8_t lowest_note_in_selection = 127;
1686 uint8_t highest_note_in_selection = 0;
1687 uint8_t highest_note_difference = 0;
1689 // find highest and lowest notes first
1691 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1692 uint8_t pitch = (*i)->note()->note();
1693 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
1694 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
1698 cerr << "dnote: " << (int) dnote << endl;
1699 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
1700 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
1701 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
1702 << int(highest_note_in_selection) << endl;
1703 cerr << "selection size: " << _selection.size() << endl;
1704 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
1707 // Make sure the note pitch does not exceed the MIDI standard range
1708 if (highest_note_in_selection + dnote > 127) {
1709 highest_note_difference = highest_note_in_selection - 127;
1712 start_diff_command(_("move notes"));
1714 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
1716 nframes64_t start_frames = beats_to_frames((*i)->note()->time());
1719 start_frames += snap_frame_to_frame(trackview.editor().pixel_to_frame(dt));
1721 start_frames -= snap_frame_to_frame(trackview.editor().pixel_to_frame(-dt));
1724 Evoral::MusicalTime new_time = frames_to_beats(start_frames);
1730 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
1732 uint8_t original_pitch = (*i)->note()->note();
1733 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
1735 // keep notes in standard midi range
1736 clamp_to_0_127(new_pitch);
1738 // keep original pitch if note is dragged outside valid midi range
1739 if ((original_pitch != 0 && new_pitch == 0)
1740 || (original_pitch != 127 && new_pitch == 127)) {
1741 new_pitch = original_pitch;
1744 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
1745 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
1747 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
1752 // care about notes being moved beyond the upper/lower bounds on the canvas
1753 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
1754 highest_note_in_selection > midi_stream_view()->highest_note()) {
1755 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
1760 MidiRegionView::snap_pixel_to_frame(double x)
1762 PublicEditor& editor = trackview.editor();
1763 // x is region relative, convert it to global absolute frames
1764 nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
1765 editor.snap_to(frame);
1766 return frame - _region->position(); // convert back to region relative
1770 MidiRegionView::snap_frame_to_frame(nframes64_t x)
1772 PublicEditor& editor = trackview.editor();
1773 // x is region relative, convert it to global absolute frames
1774 nframes64_t frame = x + _region->position();
1775 editor.snap_to(frame);
1776 return frame - _region->position(); // convert back to region relative
1780 MidiRegionView::snap_to_pixel(double x)
1782 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
1786 MidiRegionView::get_position_pixels()
1788 nframes64_t region_frame = get_position();
1789 return trackview.editor().frame_to_pixel(region_frame);
1793 MidiRegionView::get_end_position_pixels()
1795 nframes64_t frame = get_position() + get_duration ();
1796 return trackview.editor().frame_to_pixel(frame);
1800 MidiRegionView::beats_to_frames(double beats) const
1802 return _time_converter.to(beats);
1806 MidiRegionView::frames_to_beats(nframes64_t frames) const
1808 return _time_converter.from(frames);
1812 MidiRegionView::begin_resizing (bool /*at_front*/)
1814 _resize_data.clear();
1816 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1817 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
1819 // only insert CanvasNotes into the map
1821 NoteResizeData *resize_data = new NoteResizeData();
1822 resize_data->canvas_note = note;
1824 // create a new SimpleRect from the note which will be the resize preview
1825 SimpleRect *resize_rect = new SimpleRect(
1826 *group, note->x1(), note->y1(), note->x2(), note->y2());
1828 // calculate the colors: get the color settings
1829 uint32_t fill_color = UINT_RGBA_CHANGE_A(
1830 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
1833 // make the resize preview notes more transparent and bright
1834 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
1836 // calculate color based on note velocity
1837 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
1838 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity()),
1842 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
1843 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
1845 resize_data->resize_rect = resize_rect;
1846 _resize_data.push_back(resize_data);
1852 MidiRegionView::update_resizing (bool at_front, double delta_x, bool relative)
1854 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
1855 SimpleRect* resize_rect = (*i)->resize_rect;
1856 CanvasNote* canvas_note = (*i)->canvas_note;
1861 current_x = canvas_note->x1() + delta_x;
1863 // x is in track relative, transform it to region relative
1864 current_x = delta_x - get_position_pixels();
1868 current_x = canvas_note->x2() + delta_x;
1870 // x is in track relative, transform it to region relative
1871 current_x = delta_x - get_end_position_pixels ();
1876 resize_rect->property_x1() = snap_to_pixel(current_x);
1877 resize_rect->property_x2() = canvas_note->x2();
1879 resize_rect->property_x2() = snap_to_pixel(current_x);
1880 resize_rect->property_x1() = canvas_note->x1();
1886 MidiRegionView::commit_resizing (bool at_front, double delta_x, bool relative)
1888 start_diff_command(_("resize notes"));
1890 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
1891 CanvasNote* canvas_note = (*i)->canvas_note;
1892 SimpleRect* resize_rect = (*i)->resize_rect;
1893 const double region_start = get_position_pixels();
1898 current_x = canvas_note->x1() + delta_x;
1900 // x is in track relative, transform it to region relative
1901 current_x = region_start + delta_x;
1905 current_x = canvas_note->x2() + delta_x;
1907 // x is in track relative, transform it to region relative
1908 current_x = region_start + delta_x;
1912 current_x = snap_pixel_to_frame (current_x);
1913 current_x = frames_to_beats (current_x);
1915 if (at_front && current_x < canvas_note->note()->end_time()) {
1916 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
1918 double len = canvas_note->note()->time() - current_x;
1919 len += canvas_note->note()->length();
1922 /* XXX convert to beats */
1923 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
1928 double len = current_x - canvas_note->note()->time();
1931 /* XXX convert to beats */
1932 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
1940 _resize_data.clear();
1945 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
1947 uint8_t new_velocity;
1950 new_velocity = event->note()->velocity() + velocity;
1951 clamp_to_0_127(new_velocity);
1953 new_velocity = velocity;
1956 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
1960 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
1965 new_note = event->note()->note() + note;
1970 clamp_to_0_127 (new_note);
1971 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
1975 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
1977 bool change_start = false;
1978 bool change_length = false;
1979 Evoral::MusicalTime new_start;
1980 Evoral::MusicalTime new_length;
1982 /* NOTE: the semantics of the two delta arguments are slightly subtle:
1984 front_delta: if positive - move the start of the note later in time (shortening it)
1985 if negative - move the start of the note earlier in time (lengthening it)
1987 end_delta: if positive - move the end of the note later in time (lengthening it)
1988 if negative - move the end of the note earlier in time (shortening it)
1992 if (front_delta < 0) {
1994 if (event->note()->time() < -front_delta) {
1997 new_start = event->note()->time() + front_delta; // moves earlier
2000 /* start moved toward zero, so move the end point out to where it used to be.
2001 Note that front_delta is negative, so this increases the length.
2004 new_length = event->note()->length() - front_delta;
2005 change_start = true;
2006 change_length = true;
2010 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2012 if (new_pos < event->note()->end_time()) {
2013 new_start = event->note()->time() + front_delta;
2014 /* start moved toward the end, so move the end point back to where it used to be */
2015 new_length = event->note()->length() - front_delta;
2016 change_start = true;
2017 change_length = true;
2024 bool can_change = true;
2025 if (end_delta < 0) {
2026 if (event->note()->length() < -end_delta) {
2032 new_length = event->note()->length() + end_delta;
2033 change_length = true;
2038 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2041 if (change_length) {
2042 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2047 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2049 Evoral::MusicalTime new_time;
2053 if (event->note()->time() < -delta) {
2056 new_time = event->note()->time() + delta;
2059 new_time = event->note()->time() + delta;
2065 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2069 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2073 if (_selection.empty()) {
2088 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2089 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2095 start_diff_command(_("change velocities"));
2097 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2098 Selection::iterator next = i;
2100 change_note_velocity (*i, delta, true);
2109 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2111 if (_selection.empty()) {
2128 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2130 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2134 if ((int8_t) (*i)->note()->note() + delta > 127) {
2141 start_diff_command (_("transpose"));
2143 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2144 Selection::iterator next = i;
2146 change_note_note (*i, delta, true);
2154 MidiRegionView::change_note_lengths (bool fine, bool shorter, bool start, bool end)
2156 Evoral::MusicalTime delta;
2161 /* grab the current grid distance */
2163 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2165 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2166 cerr << "Grid type not available as beats - TO BE FIXED\n";
2175 start_diff_command (_("change note lengths"));
2177 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2178 Selection::iterator next = i;
2181 /* note the negation of the delta for start */
2183 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2192 MidiRegionView::nudge_notes (bool forward)
2194 if (_selection.empty()) {
2198 /* pick a note as the point along the timeline to get the nudge distance.
2199 its not necessarily the earliest note, so we may want to pull the notes out
2200 into a vector and sort before using the first one.
2203 nframes64_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2205 nframes64_t distance;
2207 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2209 /* grid is off - use nudge distance */
2211 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2217 nframes64_t next_pos = ref_point;
2220 /* XXX need check on max_frames, but that needs max_frames64 or something */
2223 if (next_pos == 0) {
2229 cerr << "ref point was " << ref_point << " next was " << next_pos;
2230 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2231 distance = ref_point - next_pos;
2232 cerr << " final is " << next_pos << " distance = " << distance << endl;
2235 if (distance == 0) {
2239 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2245 start_diff_command (_("nudge"));
2247 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2248 Selection::iterator next = i;
2250 change_note_time (*i, delta, true);
2258 MidiRegionView::change_channel(uint8_t channel)
2260 start_diff_command(_("change channel"));
2261 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2262 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2269 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2271 if (_mouse_state == SelectTouchDragging) {
2272 note_selected(ev, true);
2276 snprintf (buf, sizeof (buf), "%d", (int) ev->note()->note());
2277 // This causes an infinite loop on note add sometimes
2278 //PublicEditor& editor (trackview.editor());
2279 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
2280 //editor.show_verbose_canvas_cursor_with (buf);
2284 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2286 PublicEditor& editor (trackview.editor());
2287 editor.hide_verbose_canvas_cursor ();
2292 MidiRegionView::switch_source(boost::shared_ptr<Source> src)
2294 boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(src);
2296 display_model(msrc->model());
2300 MidiRegionView::set_frame_color()
2303 if (_selected && should_show_selection) {
2304 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2306 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2312 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2316 case FilterChannels:
2317 _force_channel = -1;
2320 _force_channel = mask;
2321 mask = 0xFFFF; // Show all notes as active (below)
2324 // Update notes for selection
2325 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2326 (*i)->on_channel_selection_change(mask);
2329 _last_channel_selection = mask;
2333 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2335 _model_name = model;
2336 _custom_device_mode = custom_device_mode;
2341 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2343 if (_selection.empty()) {
2347 PublicEditor& editor (trackview.editor());
2352 editor.get_cut_buffer().add (selection_as_cut_buffer());
2358 start_delta_command();
2360 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2365 delta_remove_note (*i);
2376 MidiRegionView::selection_as_cut_buffer () const
2380 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2381 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*((*i)->note().get()))));
2384 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2391 MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
2397 start_delta_command (_("paste"));
2399 Evoral::MusicalTime beat_delta;
2400 Evoral::MusicalTime paste_pos_beats;
2401 Evoral::MusicalTime duration;
2402 Evoral::MusicalTime end_point;
2404 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2405 paste_pos_beats = frames_to_beats (pos - _region->position());
2406 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2407 paste_pos_beats = 0;
2409 _selection.clear ();
2411 for (int n = 0; n < (int) times; ++n) {
2413 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2415 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2416 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2418 /* make all newly added notes selected */
2420 delta_add_note (copied_note, true);
2421 end_point = copied_note->end_time();
2424 paste_pos_beats += duration;
2427 /* if we pasted past the current end of the region, extend the region */
2429 nframes64_t end_frame = _region->position() + beats_to_frames (end_point);
2430 nframes64_t region_end = _region->position() + _region->length() - 1;
2432 if (end_frame > region_end) {
2434 trackview.session().begin_reversible_command (_("paste"));
2436 XMLNode& before (_region->get_state());
2437 _region->set_length (end_frame, this);
2438 trackview.session().add_command (new MementoCommand<Region>(*_region, &before, &_region->get_state()));
2444 struct EventNoteTimeEarlyFirstComparator {
2445 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2446 return a->note()->time() < b->note()->time();
2451 MidiRegionView::time_sort_events ()
2453 if (!_sort_needed) {
2457 EventNoteTimeEarlyFirstComparator cmp;
2460 _sort_needed = false;
2464 MidiRegionView::goto_next_note ()
2466 // nframes64_t pos = -1;
2467 bool use_next = false;
2469 if (_events.back()->selected()) {
2473 time_sort_events ();
2475 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2476 if ((*i)->selected()) {
2479 } else if (use_next) {
2481 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2486 /* use the first one */
2488 unique_select (_events.front());
2493 MidiRegionView::goto_previous_note ()
2495 // nframes64_t pos = -1;
2496 bool use_next = false;
2498 if (_events.front()->selected()) {
2502 time_sort_events ();
2504 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2505 if ((*i)->selected()) {
2508 } else if (use_next) {
2510 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2515 /* use the last one */
2517 unique_select (*(_events.rbegin()));
2521 MidiRegionView::selection_as_notelist (Notes& selected)
2523 time_sort_events ();
2525 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2526 if ((*i)->selected()) {
2527 selected.insert ((*i)->note());