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();
97 PublicEditor::DropDownKeys
.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys
));
100 MidiRegionView::MidiRegionView (ArdourCanvas::Group
*parent
, RouteTimeAxisView
&tv
,
101 boost::shared_ptr
<MidiRegion
> r
, double spu
, Gdk::Color
& basic_color
,
102 TimeAxisViewItem::Visibility visibility
)
103 : RegionView (parent
, tv
, r
, spu
, basic_color
, false, visibility
)
105 , _last_channel_selection(0xFFFF)
106 , _model_name(string())
107 , _custom_device_mode(string())
109 , _note_group(new ArdourCanvas::Group(*parent
))
115 , _sort_needed (true)
116 , _optimization_iterator (_events
.end())
118 , no_sound_notes (false)
120 _note_group
->raise_to_top();
121 PublicEditor::DropDownKeys
.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys
));
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 PublicEditor::DropDownKeys
.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys
));
184 CanvasNoteEvent::CanvasNoteEventDeleted
.connect (note_delete_connection
, MISSING_INVALIDATOR
,
185 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection
, this, _1
),
189 midi_region()->midi_source(0)->load_model();
192 _model
= midi_region()->midi_source(0)->model();
193 _enable_display
= false;
195 RegionView::init (basic_color
, false);
197 compute_colors (basic_color
);
199 set_height (trackview
.current_height());
202 region_sync_changed ();
203 region_resized (ARDOUR::bounds_change
);
206 reset_width_dependent_items (_pixel_width
);
210 _enable_display
= true;
213 display_model (_model
);
217 group
->raise_to_top();
218 group
->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event
), false);
220 midi_view()->signal_channel_mode_changed().connect(
221 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed
));
223 midi_view()->signal_midi_patch_settings_changed().connect(
224 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed
));
226 trackview
.editor().SnapChanged
.connect (snap_changed_connection
, invalidator (*this), ui_bind (&MidiRegionView::snap_changed
, this), gui_context ());
230 MidiRegionView::canvas_event(GdkEvent
* ev
)
232 if (!trackview
.editor().internal_editing()) {
236 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
237 to its items, which means that ev->type == GDK_SCROLL will never be seen
242 return scroll (&ev
->scroll
);
245 return key_press (&ev
->key
);
247 case GDK_KEY_RELEASE
:
248 return key_release (&ev
->key
);
250 case GDK_BUTTON_PRESS
:
251 return button_press (&ev
->button
);
253 case GDK_2BUTTON_PRESS
:
256 case GDK_BUTTON_RELEASE
:
257 return button_release (&ev
->button
);
259 case GDK_ENTER_NOTIFY
:
260 return enter_notify (&ev
->crossing
);
262 case GDK_LEAVE_NOTIFY
:
263 return leave_notify (&ev
->crossing
);
265 case GDK_MOTION_NOTIFY
:
266 return motion (&ev
->motion
);
276 MidiRegionView::enter_notify (GdkEventCrossing
* ev
)
278 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
280 Keyboard::magic_widget_grab_focus();
283 if (trackview
.editor().current_mouse_mode() == MouseRange
) {
284 create_ghost_note (ev
->x
, ev
->y
);
291 MidiRegionView::leave_notify (GdkEventCrossing
* ev
)
293 trackview
.editor().hide_verbose_canvas_cursor ();
300 MidiRegionView::button_press (GdkEventButton
* ev
)
304 group
->w2i (_last_x
, _last_y
);
306 if (_mouse_state
!= SelectTouchDragging
&& ev
->button
== 1) {
307 _pressed_button
= ev
->button
;
308 _mouse_state
= Pressed
;
312 _pressed_button
= ev
->button
;
318 MidiRegionView::button_release (GdkEventButton
* ev
)
320 double event_x
, event_y
;
321 nframes64_t event_frame
= 0;
325 group
->w2i(event_x
, event_y
);
326 group
->ungrab(ev
->time
);
327 event_frame
= trackview
.editor().pixel_to_frame(event_x
);
329 if (ev
->button
== 3) {
331 } else if (_pressed_button
!= 1) {
335 switch (_mouse_state
) {
336 case Pressed
: // Clicked
337 switch (trackview
.editor().current_mouse_mode()) {
341 maybe_select_by_position (ev
, event_x
, event_y
);
347 Evoral::MusicalTime beats
= trackview
.editor().get_grid_type_as_beats (success
, trackview
.editor().pixel_to_frame (event_x
));
351 create_note_at (event_x
, event_y
, beats
, true);
359 case SelectRectDragging
: // Select drag done
365 case AddDragging
: // Add drag done
367 if (_drag_rect
->property_x2() > _drag_rect
->property_x1() + 2) {
368 const double x
= _drag_rect
->property_x1();
369 const double length
= trackview
.editor().pixel_to_frame
370 (_drag_rect
->property_x2() - _drag_rect
->property_x1());
372 create_note_at (x
, _drag_rect
->property_y1(), frames_to_beats(length
), false);
378 create_ghost_note (ev
->x
, ev
->y
);
388 MidiRegionView::motion (GdkEventMotion
* ev
)
390 double event_x
, event_y
;
391 nframes64_t event_frame
= 0;
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
->x
, ev
->y
);
407 /* any motion immediately hides velocity text that may have been visible */
409 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
410 (*i
)->hide_velocity ();
413 switch (_mouse_state
) {
414 case Pressed
: // Maybe start a drag, if we've moved a bit
416 if (fabs (event_x
- _last_x
) < 1 && fabs (event_y
- _last_y
) < 1) {
417 /* no appreciable movement since the button was pressed */
422 if (_pressed_button
== 1 && trackview
.editor().current_mouse_mode() == MouseObject
) {
423 group
->grab(GDK_POINTER_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
,
424 Gdk::Cursor(Gdk::FLEUR
), ev
->time
);
427 _drag_start_x
= event_x
;
428 _drag_start_y
= event_y
;
430 _drag_rect
= new ArdourCanvas::SimpleRect(*group
);
431 _drag_rect
->property_x1() = event_x
;
432 _drag_rect
->property_y1() = event_y
;
433 _drag_rect
->property_x2() = event_x
;
434 _drag_rect
->property_y2() = event_y
;
435 _drag_rect
->property_outline_what() = 0xFF;
436 _drag_rect
->property_outline_color_rgba()
437 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline
.get();
438 _drag_rect
->property_fill_color_rgba()
439 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill
.get();
441 _mouse_state
= SelectRectDragging
;
444 // Add note drag start
445 } else if (trackview
.editor().internal_editing()) {
450 group
->grab(GDK_POINTER_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
,
451 Gdk::Cursor(Gdk::FLEUR
), ev
->time
);
454 _drag_start_x
= event_x
;
455 _drag_start_y
= event_y
;
457 _drag_rect
= new ArdourCanvas::SimpleRect(*group
);
458 _drag_rect
->property_x1() = trackview
.editor().frame_to_pixel(event_frame
);
460 _drag_rect
->property_y1() = midi_stream_view()->note_to_y(
461 midi_stream_view()->y_to_note(event_y
));
462 _drag_rect
->property_x2() = trackview
.editor().frame_to_pixel(event_frame
);
463 _drag_rect
->property_y2() = _drag_rect
->property_y1()
464 + floor(midi_stream_view()->note_height());
465 _drag_rect
->property_outline_what() = 0xFF;
466 _drag_rect
->property_outline_color_rgba() = 0xFFFFFF99;
467 _drag_rect
->property_fill_color_rgba() = 0xFFFFFF66;
469 _mouse_state
= AddDragging
;
475 case SelectRectDragging
: // Select drag motion
476 case AddDragging
: // Add note drag motion
480 GdkModifierType state
;
481 gdk_window_get_pointer(ev
->window
, &t_x
, &t_y
, &state
);
486 if (_mouse_state
== AddDragging
)
487 event_x
= trackview
.editor().frame_to_pixel(event_frame
);
490 if (event_x
> _drag_start_x
)
491 _drag_rect
->property_x2() = event_x
;
493 _drag_rect
->property_x1() = event_x
;
496 if (_drag_rect
&& _mouse_state
== SelectRectDragging
) {
497 if (event_y
> _drag_start_y
)
498 _drag_rect
->property_y2() = event_y
;
500 _drag_rect
->property_y1() = event_y
;
502 update_drag_selection(_drag_start_x
, event_x
, _drag_start_y
, event_y
);
508 case SelectTouchDragging
:
520 MidiRegionView::scroll (GdkEventScroll
* ev
)
522 if (_selection
.empty()) {
526 trackview
.editor().hide_verbose_canvas_cursor ();
528 bool fine
= Keyboard::modifier_state_equals (ev
->state
, Keyboard::SecondaryModifier
);
530 if (ev
->direction
== GDK_SCROLL_UP
) {
531 change_velocities (true, fine
, false);
532 } else if (ev
->direction
== GDK_SCROLL_DOWN
) {
533 change_velocities (false, fine
, false);
539 MidiRegionView::key_press (GdkEventKey
* ev
)
541 cerr
<< "MRV key press\n";
543 /* since GTK bindings are generally activated on press, and since
544 detectable auto-repeat is the name of the game and only sends
545 repeated presses, carry out key actions at key press, not release.
548 if (ev
->keyval
== GDK_Alt_L
|| ev
->keyval
== GDK_Alt_R
){
549 _mouse_state
= SelectTouchDragging
;
552 } else if (ev
->keyval
== GDK_Escape
) {
556 } else if (ev
->keyval
== GDK_comma
|| ev
->keyval
== GDK_period
) {
558 bool start
= (ev
->keyval
== GDK_comma
);
559 bool end
= (ev
->keyval
== GDK_period
);
560 bool shorter
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
);
561 bool fine
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
563 change_note_lengths (fine
, shorter
, start
, end
);
567 } else if (ev
->keyval
== GDK_Delete
) {
572 } else if (ev
->keyval
== GDK_Tab
) {
574 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
575 goto_previous_note ();
581 } else if (ev
->keyval
== GDK_Up
) {
583 bool allow_smush
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::TertiaryModifier
);
584 bool fine
= !Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
586 if (Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
)) {
587 change_velocities (true, fine
, allow_smush
);
589 transpose (true, fine
, allow_smush
);
593 } else if (ev
->keyval
== GDK_Down
) {
595 bool allow_smush
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::TertiaryModifier
);
596 bool fine
= !Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
598 if (Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
)) {
599 change_velocities (false, fine
, allow_smush
);
601 transpose (false, fine
, allow_smush
);
605 } else if (ev
->keyval
== GDK_Left
) {
610 } else if (ev
->keyval
== GDK_Right
) {
615 } else if (ev
->keyval
== GDK_Control_L
) {
618 } else if (ev
->keyval
== GDK_r
) {
619 /* yes, this steals r */
620 if (midi_view()->midi_track()->step_editing()) {
621 midi_view()->step_edit_rest ();
622 cerr
<< "Stole that r because " << midi_view()->midi_track()->name()
623 << " is step editing!\n";
632 MidiRegionView::key_release (GdkEventKey
* ev
)
634 if (ev
->keyval
== GDK_Alt_L
|| ev
->keyval
== GDK_Alt_R
) {
642 MidiRegionView::show_list_editor ()
645 _list_editor
= new MidiListEditor (trackview
.session(), midi_region());
647 _list_editor
->present ();
650 /** Add a note to the model, and the view, at a canvas (click) coordinate.
651 * \param x horizontal position in pixels
652 * \param y vertical position in pixels
653 * \param length duration of the note in beats, which will be snapped to the grid
654 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
657 MidiRegionView::create_note_at(double x
, double y
, double length
, bool sh
)
659 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
660 MidiStreamView
* const view
= mtv
->midi_view();
662 double note
= midi_stream_view()->y_to_note(y
);
665 assert(note
<= 127.0);
667 // Start of note in frames relative to region start
668 nframes64_t
const start_frames
= snap_frame_to_frame(trackview
.editor().pixel_to_frame(x
));
669 assert(start_frames
>= 0);
672 length
= frames_to_beats(
673 snap_frame_to_frame(start_frames
+ beats_to_frames(length
)) - start_frames
);
675 assert (length
!= 0);
678 length
= frames_to_beats (beats_to_frames (length
) - 1);
681 const boost::shared_ptr
<NoteType
> new_note(new NoteType(0,
682 frames_to_beats(start_frames
+ _region
->start()), length
,
683 (uint8_t)note
, 0x40));
685 if (_model
->contains (new_note
)) {
689 view
->update_note_range(new_note
->note());
691 MidiModel::DiffCommand
* cmd
= _model
->new_diff_command("add note");
693 _model
->apply_command(*trackview
.session(), cmd
);
695 play_midi_note (new_note
);
699 MidiRegionView::clear_events()
704 for (std::vector
<GhostRegion
*>::iterator g
= ghosts
.begin(); g
!= ghosts
.end(); ++g
) {
705 if ((gr
= dynamic_cast<MidiGhostRegion
*>(*g
)) != 0) {
710 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
715 _pgm_changes
.clear();
717 _optimization_iterator
= _events
.end();
722 MidiRegionView::display_model(boost::shared_ptr
<MidiModel
> model
)
725 content_connection
.disconnect ();
726 _model
->ContentsChanged
.connect (content_connection
, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model
, this), gui_context());
730 if (_enable_display
) {
736 MidiRegionView::start_diff_command(string name
)
738 if (!_diff_command
) {
739 _diff_command
= _model
->new_diff_command(name
);
744 MidiRegionView::diff_add_note(const boost::shared_ptr
<NoteType
> note
, bool selected
, bool show_velocity
)
747 _diff_command
->add(note
);
750 _marked_for_selection
.insert(note
);
753 _marked_for_velocity
.insert(note
);
758 MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent
* ev
)
760 if (_diff_command
&& ev
->note()) {
761 _diff_command
->remove(ev
->note());
766 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent
* ev
,
767 MidiModel::DiffCommand::Property property
,
771 _diff_command
->change (ev
->note(), property
, val
);
776 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent
* ev
,
777 MidiModel::DiffCommand::Property property
,
778 Evoral::MusicalTime val
)
781 _diff_command
->change (ev
->note(), property
, val
);
786 MidiRegionView::apply_diff ()
790 if (!_diff_command
) {
794 if ((add_or_remove
= _diff_command
->adds_or_removes())) {
795 // Mark all selected notes for selection when model reloads
796 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
797 _marked_for_selection
.insert((*i
)->note());
801 _model
->apply_command(*trackview
.session(), _diff_command
);
803 midi_view()->midi_track()->playlist_modified();
807 _marked_for_selection
.clear();
810 _marked_for_velocity
.clear();
814 MidiRegionView::apply_diff_as_subcommand()
818 if (!_diff_command
) {
822 if ((add_or_remove
= _diff_command
->adds_or_removes())) {
823 // Mark all selected notes for selection when model reloads
824 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
825 _marked_for_selection
.insert((*i
)->note());
829 _model
->apply_command_as_subcommand(*trackview
.session(), _diff_command
);
831 midi_view()->midi_track()->playlist_modified();
834 _marked_for_selection
.clear();
836 _marked_for_velocity
.clear();
841 MidiRegionView::abort_command()
843 delete _diff_command
;
849 MidiRegionView::find_canvas_note (boost::shared_ptr
<NoteType
> note
)
851 if (_optimization_iterator
!= _events
.end()) {
852 ++_optimization_iterator
;
855 if (_optimization_iterator
!= _events
.end() && (*_optimization_iterator
)->note() == note
) {
856 return *_optimization_iterator
;
859 for (_optimization_iterator
= _events
.begin(); _optimization_iterator
!= _events
.end(); ++_optimization_iterator
) {
860 if ((*_optimization_iterator
)->note() == note
) {
861 return *_optimization_iterator
;
869 MidiRegionView::get_events (Events
& e
, Evoral::Sequence
<Evoral::MusicalTime
>::NoteOperator op
, uint8_t val
, int chan_mask
)
871 MidiModel::Notes notes
;
872 _model
->get_notes (notes
, op
, val
, chan_mask
);
874 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
875 CanvasNoteEvent
* cne
= find_canvas_note (*n
);
883 MidiRegionView::redisplay_model()
885 // Don't redisplay the model if we're currently recording and displaying that
891 cerr
<< "MidiRegionView::redisplay_model called without a model" << endmsg
;
895 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
899 MidiModel::ReadLock
lock(_model
->read_lock());
901 MidiModel::Notes
& notes (_model
->notes());
902 _optimization_iterator
= _events
.begin();
904 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
906 boost::shared_ptr
<NoteType
> note (*n
);
907 CanvasNoteEvent
* cne
;
910 if (note_in_region_range (note
, visible
)) {
912 if ((cne
= find_canvas_note (note
)) != 0) {
919 if ((cn
= dynamic_cast<CanvasNote
*>(cne
)) != 0) {
921 } else if ((ch
= dynamic_cast<CanvasHit
*>(cne
)) != 0) {
933 add_note (note
, visible
);
938 if ((cne
= find_canvas_note (note
)) != 0) {
946 /* remove note items that are no longer valid */
948 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ) {
949 if (!(*i
)->valid ()) {
951 i
= _events
.erase (i
);
958 display_program_changes();
960 _marked_for_selection
.clear ();
961 _marked_for_velocity
.clear ();
963 /* we may have caused _events to contain things out of order (e.g. if a note
964 moved earlier or later). we don't generally need them in time order, but
965 make a note that a sort is required for those cases that require it.
972 MidiRegionView::display_program_changes()
974 boost::shared_ptr
<Evoral::Control
> control
= _model
->control(MidiPgmChangeAutomation
);
979 Glib::Mutex::Lock
lock (control
->list()->lock());
981 uint8_t channel
= control
->parameter().channel();
983 for (AutomationList::const_iterator event
= control
->list()->begin();
984 event
!= control
->list()->end(); ++event
) {
985 double event_time
= (*event
)->when
;
986 double program_number
= floor((*event
)->value
+ 0.5);
988 // Get current value of bank select MSB at time of the program change
989 Evoral::Parameter
bank_select_msb(MidiCCAutomation
, channel
, MIDI_CTL_MSB_BANK
);
990 boost::shared_ptr
<Evoral::Control
> msb_control
= _model
->control(bank_select_msb
);
992 if (msb_control
!= 0) {
993 msb
= uint8_t(floor(msb_control
->get_float(true, event_time
) + 0.5));
996 // Get current value of bank select LSB at time of the program change
997 Evoral::Parameter
bank_select_lsb(MidiCCAutomation
, channel
, MIDI_CTL_LSB_BANK
);
998 boost::shared_ptr
<Evoral::Control
> lsb_control
= _model
->control(bank_select_lsb
);
1000 if (lsb_control
!= 0) {
1001 lsb
= uint8_t(floor(lsb_control
->get_float(true, event_time
) + 0.5));
1004 MIDI::Name::PatchPrimaryKey
patch_key(msb
, lsb
, program_number
);
1006 boost::shared_ptr
<MIDI::Name::Patch
> patch
=
1007 MIDI::Name::MidiPatchManager::instance().find_patch(
1008 _model_name
, _custom_device_mode
, channel
, patch_key
);
1010 PCEvent
program_change(event_time
, uint8_t(program_number
), channel
);
1013 add_pgm_change(program_change
, patch
->name());
1016 snprintf(buf
, 4, "%d", int(program_number
));
1017 add_pgm_change(program_change
, buf
);
1023 MidiRegionView::display_sysexes()
1025 for (MidiModel::SysExes::const_iterator i
= _model
->sysexes().begin(); i
!= _model
->sysexes().end(); ++i
) {
1026 Evoral::MusicalTime time
= (*i
)->time();
1031 for (uint32_t b
= 0; b
< (*i
)->size(); ++b
) {
1032 str
<< int((*i
)->buffer()[b
]);
1033 if (b
!= (*i
)->size() -1) {
1037 string text
= str
.str();
1039 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
1041 const double x
= trackview
.editor().frame_to_pixel(beats_to_frames(time
));
1043 double height
= midi_stream_view()->contents_height();
1045 boost::shared_ptr
<CanvasSysEx
> sysex
= boost::shared_ptr
<CanvasSysEx
>(
1046 new CanvasSysEx(*this, *group
, text
, height
, x
, 1.0));
1048 // Show unless program change is beyond the region bounds
1049 if (time
- _region
->start() >= _region
->length() || time
< _region
->start()) {
1055 _sys_exes
.push_back(sysex
);
1060 MidiRegionView::~MidiRegionView ()
1062 in_destructor
= true;
1064 note_delete_connection
.disconnect ();
1066 delete _list_editor
;
1068 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1070 if (_active_notes
) {
1077 delete _diff_command
;
1081 MidiRegionView::region_resized (const PropertyChange
& what_changed
)
1083 RegionView::region_resized(what_changed
);
1085 if (what_changed
.contains (ARDOUR::Properties::position
)) {
1086 set_duration(_region
->length(), 0);
1087 if (_enable_display
) {
1094 MidiRegionView::reset_width_dependent_items (double pixel_width
)
1096 RegionView::reset_width_dependent_items(pixel_width
);
1097 assert(_pixel_width
== pixel_width
);
1099 if (_enable_display
) {
1105 MidiRegionView::set_height (double height
)
1107 static const double FUDGE
= 2.0;
1108 const double old_height
= _height
;
1109 RegionView::set_height(height
);
1110 _height
= height
- FUDGE
;
1112 apply_note_range(midi_stream_view()->lowest_note(),
1113 midi_stream_view()->highest_note(),
1114 height
!= old_height
+ FUDGE
);
1117 name_pixbuf
->raise_to_top();
1122 /** Apply the current note range from the stream view
1123 * by repositioning/hiding notes as necessary
1126 MidiRegionView::apply_note_range (uint8_t min
, uint8_t max
, bool force
)
1128 if (!_enable_display
) {
1132 if (!force
&& _current_range_min
== min
&& _current_range_max
== max
) {
1136 _current_range_min
= min
;
1137 _current_range_max
= max
;
1139 for (Events::const_iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1140 CanvasNoteEvent
* event
= *i
;
1141 boost::shared_ptr
<NoteType
> note (event
->note());
1143 if (note
->note() < _current_range_min
||
1144 note
->note() > _current_range_max
) {
1150 if (CanvasNote
* cnote
= dynamic_cast<CanvasNote
*>(event
)) {
1152 const double y1
= midi_stream_view()->note_to_y(note
->note());
1153 const double y2
= y1
+ floor(midi_stream_view()->note_height());
1155 cnote
->property_y1() = y1
;
1156 cnote
->property_y2() = y2
;
1158 } else if (CanvasHit
* chit
= dynamic_cast<CanvasHit
*>(event
)) {
1160 double x
= trackview
.editor().frame_to_pixel(
1161 beats_to_frames(note
->time()) - _region
->start());
1162 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1163 double y
= midi_stream_view()->note_to_y(event
->note()->note())
1164 + ((diamond_size
-2.0) / 4.0);
1166 chit
->set_height (diamond_size
);
1167 chit
->move (x
- chit
->x1(), y
- chit
->y1());
1174 MidiRegionView::add_ghost (TimeAxisView
& tv
)
1178 double unit_position
= _region
->position () / samples_per_unit
;
1179 MidiTimeAxisView
* mtv
= dynamic_cast<MidiTimeAxisView
*>(&tv
);
1180 MidiGhostRegion
* ghost
;
1182 if (mtv
&& mtv
->midi_view()) {
1183 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1184 to allow having midi notes on top of note lines and waveforms.
1186 ghost
= new MidiGhostRegion (*mtv
->midi_view(), trackview
, unit_position
);
1188 ghost
= new MidiGhostRegion (tv
, trackview
, unit_position
);
1191 ghost
->set_height ();
1192 ghost
->set_duration (_region
->length() / samples_per_unit
);
1193 ghosts
.push_back (ghost
);
1195 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1196 if ((note
= dynamic_cast<CanvasNote
*>(*i
)) != 0) {
1197 ghost
->add_note(note
);
1201 GhostRegion::CatchDeletion
.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost
, this, _1
), gui_context());
1207 /** Begin tracking note state for successive calls to add_event
1210 MidiRegionView::begin_write()
1212 assert(!_active_notes
);
1213 _active_notes
= new CanvasNote
*[128];
1214 for (unsigned i
=0; i
< 128; ++i
) {
1215 _active_notes
[i
] = 0;
1220 /** Destroy note state for add_event
1223 MidiRegionView::end_write()
1225 delete[] _active_notes
;
1227 _marked_for_selection
.clear();
1228 _marked_for_velocity
.clear();
1232 /** Resolve an active MIDI note (while recording).
1235 MidiRegionView::resolve_note(uint8_t note
, double end_time
)
1237 if (midi_view()->note_mode() != Sustained
) {
1241 if (_active_notes
&& _active_notes
[note
]) {
1242 const nframes64_t end_time_frames
= beats_to_frames(end_time
);
1243 _active_notes
[note
]->property_x2() = trackview
.editor().frame_to_pixel(end_time_frames
);
1244 _active_notes
[note
]->property_outline_what() = (guint32
) 0xF; // all edges
1245 _active_notes
[note
] = 0;
1250 /** Extend active notes to rightmost edge of region (if length is changed)
1253 MidiRegionView::extend_active_notes()
1255 if (!_active_notes
) {
1259 for (unsigned i
=0; i
< 128; ++i
) {
1260 if (_active_notes
[i
]) {
1261 _active_notes
[i
]->property_x2() = trackview
.editor().frame_to_pixel(_region
->length());
1267 MidiRegionView::play_midi_note(boost::shared_ptr
<NoteType
> note
)
1269 if (no_sound_notes
|| !trackview
.editor().sound_notes()) {
1273 RouteUI
* route_ui
= dynamic_cast<RouteUI
*> (&trackview
);
1276 route_ui
->midi_track()->write_immediate_event(
1277 note
->on_event().size(), note
->on_event().buffer());
1279 const double note_length_beats
= (note
->off_event().time() - note
->on_event().time());
1280 nframes_t note_length_ms
= beats_to_frames(note_length_beats
)
1281 * (1000 / (double)route_ui
->session()->nominal_frame_rate());
1282 Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(this, &MidiRegionView::play_midi_note_off
), note
),
1283 note_length_ms
, G_PRIORITY_DEFAULT
);
1287 MidiRegionView::play_midi_note_off(boost::shared_ptr
<NoteType
> note
)
1289 RouteUI
* route_ui
= dynamic_cast<RouteUI
*> (&trackview
);
1292 route_ui
->midi_track()->write_immediate_event(
1293 note
->off_event().size(), note
->off_event().buffer());
1299 MidiRegionView::note_in_region_range(const boost::shared_ptr
<NoteType
> note
, bool& visible
) const
1301 const nframes64_t note_start_frames
= beats_to_frames(note
->time());
1303 bool outside
= (note_start_frames
- _region
->start() >= _region
->length()) ||
1304 (note_start_frames
< _region
->start());
1306 visible
= (note
->note() >= midi_stream_view()->lowest_note()) &&
1307 (note
->note() <= midi_stream_view()->highest_note());
1313 MidiRegionView::update_note (CanvasNote
* ev
)
1315 boost::shared_ptr
<NoteType
> note
= ev
->note();
1317 const nframes64_t note_start_frames
= beats_to_frames(note
->time());
1318 const nframes64_t note_end_frames
= beats_to_frames(note
->end_time());
1320 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1321 const double y1
= midi_stream_view()->note_to_y(note
->note());
1322 const double note_endpixel
= trackview
.editor().frame_to_pixel(note_end_frames
- _region
->start());
1324 ev
->property_x1() = x
;
1325 ev
->property_y1() = y1
;
1326 if (note
->length() > 0) {
1327 ev
->property_x2() = note_endpixel
;
1329 ev
->property_x2() = trackview
.editor().frame_to_pixel(_region
->length());
1331 ev
->property_y2() = y1
+ floor(midi_stream_view()->note_height());
1333 if (note
->length() == 0) {
1334 if (_active_notes
) {
1335 assert(note
->note() < 128);
1336 // If this note is already active there's a stuck note,
1337 // finish the old note rectangle
1338 if (_active_notes
[note
->note()]) {
1339 CanvasNote
* const old_rect
= _active_notes
[note
->note()];
1340 boost::shared_ptr
<NoteType
> old_note
= old_rect
->note();
1341 old_rect
->property_x2() = x
;
1342 old_rect
->property_outline_what() = (guint32
) 0xF;
1344 _active_notes
[note
->note()] = ev
;
1346 /* outline all but right edge */
1347 ev
->property_outline_what() = (guint32
) (0x1 & 0x4 & 0x8);
1349 /* outline all edges */
1350 ev
->property_outline_what() = (guint32
) 0xF;
1355 MidiRegionView::update_hit (CanvasHit
* ev
)
1357 boost::shared_ptr
<NoteType
> note
= ev
->note();
1359 const nframes64_t note_start_frames
= beats_to_frames(note
->time());
1360 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1361 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1362 const double y
= midi_stream_view()->note_to_y(note
->note()) + ((diamond_size
-2) / 4.0);
1367 /** Add a MIDI note to the view (with length).
1369 * If in sustained mode, notes with length 0 will be considered active
1370 * notes, and resolve_note should be called when the corresponding note off
1371 * event arrives, to properly display the note.
1374 MidiRegionView::add_note(const boost::shared_ptr
<NoteType
> note
, bool visible
)
1376 CanvasNoteEvent
* event
= 0;
1378 assert(note
->time() >= 0);
1379 assert(midi_view()->note_mode() == Sustained
|| midi_view()->note_mode() == Percussive
);
1381 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
1383 if (midi_view()->note_mode() == Sustained
) {
1385 CanvasNote
* ev_rect
= new CanvasNote(*this, *group
, note
);
1387 update_note (ev_rect
);
1391 MidiGhostRegion
* gr
;
1393 for (std::vector
<GhostRegion
*>::iterator g
= ghosts
.begin(); g
!= ghosts
.end(); ++g
) {
1394 if ((gr
= dynamic_cast<MidiGhostRegion
*>(*g
)) != 0) {
1395 gr
->add_note(ev_rect
);
1399 } else if (midi_view()->note_mode() == Percussive
) {
1401 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1403 CanvasHit
* ev_diamond
= new CanvasHit(*this, *group
, diamond_size
, note
);
1405 update_hit (ev_diamond
);
1414 if (_marked_for_selection
.find(note
) != _marked_for_selection
.end()) {
1415 note_selected(event
, true);
1418 if (_marked_for_velocity
.find(note
) != _marked_for_velocity
.end()) {
1419 event
->show_velocity();
1421 event
->on_channel_selection_change(_last_channel_selection
);
1422 _events
.push_back(event
);
1433 MidiRegionView::add_note (uint8_t channel
, uint8_t number
, uint8_t velocity
,
1434 Evoral::MusicalTime pos
, Evoral::MusicalTime len
)
1436 boost::shared_ptr
<NoteType
> new_note (new NoteType (channel
, pos
, len
, number
, velocity
));
1438 start_diff_command (_("step add"));
1439 diff_add_note (new_note
, true, false);
1442 /* potentially extend region to hold new note */
1444 nframes64_t end_frame
= _region
->position() + beats_to_frames (new_note
->end_time());
1445 nframes64_t region_end
= _region
->position() + _region
->length() - 1;
1447 if (end_frame
> region_end
) {
1448 _region
->set_length (end_frame
, this);
1455 MidiRegionView::add_pgm_change(PCEvent
& program
, const string
& displaytext
)
1457 assert(program
.time
>= 0);
1459 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
1460 const double x
= trackview
.editor().frame_to_pixel(beats_to_frames(program
.time
));
1462 double height
= midi_stream_view()->contents_height();
1464 boost::shared_ptr
<CanvasProgramChange
> pgm_change
= boost::shared_ptr
<CanvasProgramChange
>(
1465 new CanvasProgramChange(*this, *group
,
1470 _custom_device_mode
,
1471 program
.time
, program
.channel
, program
.value
));
1473 // Show unless program change is beyond the region bounds
1474 if (program
.time
- _region
->start() >= _region
->length() || program
.time
< _region
->start()) {
1480 _pgm_changes
.push_back(pgm_change
);
1484 MidiRegionView::get_patch_key_at(double time
, uint8_t channel
, MIDI::Name::PatchPrimaryKey
& key
)
1486 cerr
<< "getting patch key at " << time
<< " for channel " << channel
<< endl
;
1487 Evoral::Parameter
bank_select_msb(MidiCCAutomation
, channel
, MIDI_CTL_MSB_BANK
);
1488 boost::shared_ptr
<Evoral::Control
> msb_control
= _model
->control(bank_select_msb
);
1490 if (msb_control
!= 0) {
1491 msb
= int(msb_control
->get_float(true, time
));
1492 cerr
<< "got msb " << msb
;
1495 Evoral::Parameter
bank_select_lsb(MidiCCAutomation
, channel
, MIDI_CTL_LSB_BANK
);
1496 boost::shared_ptr
<Evoral::Control
> lsb_control
= _model
->control(bank_select_lsb
);
1498 if (lsb_control
!= 0) {
1499 lsb
= lsb_control
->get_float(true, time
);
1500 cerr
<< " got lsb " << lsb
;
1503 Evoral::Parameter
program_change(MidiPgmChangeAutomation
, channel
, 0);
1504 boost::shared_ptr
<Evoral::Control
> program_control
= _model
->control(program_change
);
1505 float program_number
= -1.0;
1506 if (program_control
!= 0) {
1507 program_number
= program_control
->get_float(true, time
);
1508 cerr
<< " got program " << program_number
<< endl
;
1511 key
.msb
= (int) floor(msb
+ 0.5);
1512 key
.lsb
= (int) floor(lsb
+ 0.5);
1513 key
.program_number
= (int) floor(program_number
+ 0.5);
1514 assert(key
.is_sane());
1519 MidiRegionView::alter_program_change(PCEvent
& old_program
, const MIDI::Name::PatchPrimaryKey
& new_patch
)
1521 // TODO: Get the real event here and alter them at the original times
1522 Evoral::Parameter
bank_select_msb(MidiCCAutomation
, old_program
.channel
, MIDI_CTL_MSB_BANK
);
1523 boost::shared_ptr
<Evoral::Control
> msb_control
= _model
->control(bank_select_msb
);
1524 if (msb_control
!= 0) {
1525 msb_control
->set_float(float(new_patch
.msb
), true, old_program
.time
);
1528 // TODO: Get the real event here and alter them at the original times
1529 Evoral::Parameter
bank_select_lsb(MidiCCAutomation
, old_program
.channel
, MIDI_CTL_LSB_BANK
);
1530 boost::shared_ptr
<Evoral::Control
> lsb_control
= _model
->control(bank_select_lsb
);
1531 if (lsb_control
!= 0) {
1532 lsb_control
->set_float(float(new_patch
.lsb
), true, old_program
.time
);
1535 Evoral::Parameter
program_change(MidiPgmChangeAutomation
, old_program
.channel
, 0);
1536 boost::shared_ptr
<Evoral::Control
> program_control
= _model
->control(program_change
);
1538 assert(program_control
!= 0);
1539 program_control
->set_float(float(new_patch
.program_number
), true, old_program
.time
);
1545 MidiRegionView::program_selected(CanvasProgramChange
& program
, const MIDI::Name::PatchPrimaryKey
& new_patch
)
1547 PCEvent
program_change_event(program
.event_time(), program
.program(), program
.channel());
1548 alter_program_change(program_change_event
, new_patch
);
1552 MidiRegionView::previous_program(CanvasProgramChange
& program
)
1554 MIDI::Name::PatchPrimaryKey key
;
1555 get_patch_key_at(program
.event_time(), program
.channel(), key
);
1557 boost::shared_ptr
<MIDI::Name::Patch
> patch
=
1558 MIDI::Name::MidiPatchManager::instance().previous_patch(
1560 _custom_device_mode
,
1564 PCEvent
program_change_event(program
.event_time(), program
.program(), program
.channel());
1566 alter_program_change(program_change_event
, patch
->patch_primary_key());
1571 MidiRegionView::next_program(CanvasProgramChange
& program
)
1573 MIDI::Name::PatchPrimaryKey key
;
1574 get_patch_key_at(program
.event_time(), program
.channel(), key
);
1576 boost::shared_ptr
<MIDI::Name::Patch
> patch
=
1577 MIDI::Name::MidiPatchManager::instance().next_patch(
1579 _custom_device_mode
,
1583 PCEvent
program_change_event(program
.event_time(), program
.program(), program
.channel());
1585 alter_program_change(program_change_event
, patch
->patch_primary_key());
1590 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent
* cne
)
1592 if (_selection
.empty()) {
1596 if (_selection
.erase (cne
) > 0) {
1597 cerr
<< "Erased a CNE from selection\n";
1602 MidiRegionView::delete_selection()
1604 if (_selection
.empty()) {
1608 start_diff_command (_("delete selection"));
1610 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1611 if ((*i
)->selected()) {
1612 _diff_command
->remove((*i
)->note());
1622 MidiRegionView::delete_note (boost::shared_ptr
<NoteType
> n
)
1624 start_diff_command (_("delete note"));
1625 _diff_command
->remove (n
);
1628 trackview
.editor().hide_verbose_canvas_cursor ();
1632 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent
* ev
)
1634 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1635 if ((*i
)->selected() && (*i
) != ev
) {
1636 (*i
)->selected(false);
1637 (*i
)->hide_velocity();
1645 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent
* ev
)
1647 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
1650 Selection::iterator tmp
= i
;
1653 (*i
)->selected (false);
1654 _selection
.erase (i
);
1663 /* don't bother with removing this regionview from the editor selection,
1664 since we're about to add another note, and thus put/keep this
1665 regionview in the editor selection.
1668 if (!ev
->selected()) {
1669 add_to_selection (ev
);
1674 MidiRegionView::select_matching_notes (uint8_t notenum
, uint16_t channel_mask
, bool add
, bool extend
)
1676 uint8_t low_note
= 127;
1677 uint8_t high_note
= 0;
1678 MidiModel::Notes
& notes (_model
->notes());
1679 _optimization_iterator
= _events
.begin();
1685 if (extend
&& _selection
.empty()) {
1691 /* scan existing selection to get note range */
1693 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1694 if ((*i
)->note()->note() < low_note
) {
1695 low_note
= (*i
)->note()->note();
1697 if ((*i
)->note()->note() > high_note
) {
1698 high_note
= (*i
)->note()->note();
1702 low_note
= min (low_note
, notenum
);
1703 high_note
= max (high_note
, notenum
);
1706 no_sound_notes
= true;
1708 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1710 boost::shared_ptr
<NoteType
> note (*n
);
1711 CanvasNoteEvent
* cne
;
1712 bool select
= false;
1714 if (((1 << note
->channel()) & channel_mask
) != 0) {
1716 if ((note
->note() >= low_note
&& note
->note() <= high_note
)) {
1719 } else if (note
->note() == notenum
) {
1725 if ((cne
= find_canvas_note (note
)) != 0) {
1726 // extend is false because we've taken care of it,
1727 // since it extends by time range, not pitch.
1728 note_selected (cne
, add
, false);
1732 add
= true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1736 no_sound_notes
= false;
1740 MidiRegionView::toggle_matching_notes (uint8_t notenum
, uint16_t channel_mask
)
1742 MidiModel::Notes
& notes (_model
->notes());
1743 _optimization_iterator
= _events
.begin();
1745 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1747 boost::shared_ptr
<NoteType
> note (*n
);
1748 CanvasNoteEvent
* cne
;
1750 if (note
->note() == notenum
&& (((0x0001 << note
->channel()) & channel_mask
) != 0)) {
1751 if ((cne
= find_canvas_note (note
)) != 0) {
1752 if (cne
->selected()) {
1753 note_deselected (cne
);
1755 note_selected (cne
, true, false);
1763 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent
* ev
, bool add
, bool extend
)
1766 clear_selection_except(ev
);
1771 if (!ev
->selected()) {
1772 add_to_selection (ev
);
1776 /* find end of latest note selected, select all between that and the start of "ev" */
1778 Evoral::MusicalTime earliest
= DBL_MAX
;
1779 Evoral::MusicalTime latest
= 0;
1781 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1782 if ((*i
)->note()->end_time() > latest
) {
1783 latest
= (*i
)->note()->end_time();
1785 if ((*i
)->note()->time() < earliest
) {
1786 earliest
= (*i
)->note()->time();
1790 if (ev
->note()->end_time() > latest
) {
1791 latest
= ev
->note()->end_time();
1794 if (ev
->note()->time() < earliest
) {
1795 earliest
= ev
->note()->time();
1798 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1800 /* find notes entirely within OR spanning the earliest..latest range */
1802 if (((*i
)->note()->time() >= earliest
&& (*i
)->note()->end_time() <= latest
) ||
1803 ((*i
)->note()->time() <= earliest
&& (*i
)->note()->end_time() >= latest
)) {
1804 add_to_selection (*i
);
1808 /* if events were guaranteed to be time sorted, we could do this.
1809 but as of sept 10th 2009, they no longer are.
1812 if ((*i
)->note()->time() > latest
) {
1821 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent
* ev
)
1823 remove_from_selection (ev
);
1827 MidiRegionView::update_drag_selection(double x1
, double x2
, double y1
, double y2
)
1837 // TODO: Make this faster by storing the last updated selection rect, and only
1838 // adjusting things that are in the area that appears/disappeared.
1839 // We probably need a tree to be able to find events in O(log(n)) time.
1841 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1843 /* check if any corner of the note is inside the rect
1846 1) this is computing "touched by", not "contained by" the rect.
1847 2) this does not require that events be sorted in time.
1850 const double ix1
= (*i
)->x1();
1851 const double ix2
= (*i
)->x2();
1852 const double iy1
= (*i
)->y1();
1853 const double iy2
= (*i
)->y2();
1855 if ((ix1
>= x1
&& ix1
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
1856 (ix1
>= x1
&& ix1
<= x2
&& iy2
>= y1
&& iy2
<= y2
) ||
1857 (ix2
>= x1
&& ix2
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
1858 (ix2
>= x1
&& ix2
<= x2
&& iy2
>= y1
&& iy2
<= y2
)) {
1861 if (!(*i
)->selected()) {
1862 add_to_selection (*i
);
1864 } else if ((*i
)->selected()) {
1865 // Not inside rectangle
1866 remove_from_selection (*i
);
1872 MidiRegionView::remove_from_selection (CanvasNoteEvent
* ev
)
1874 Selection::iterator i
= _selection
.find (ev
);
1876 if (i
!= _selection
.end()) {
1877 _selection
.erase (i
);
1880 ev
->selected (false);
1881 ev
->hide_velocity ();
1883 if (_selection
.empty()) {
1884 PublicEditor
& editor (trackview
.editor());
1885 editor
.get_selection().remove (this);
1890 MidiRegionView::add_to_selection (CanvasNoteEvent
* ev
)
1892 bool add_mrv_selection
= false;
1894 if (_selection
.empty()) {
1895 add_mrv_selection
= true;
1898 if (_selection
.insert (ev
).second
) {
1899 ev
->selected (true);
1900 play_midi_note ((ev
)->note());
1903 if (add_mrv_selection
) {
1904 PublicEditor
& editor (trackview
.editor());
1905 editor
.get_selection().add (this);
1910 MidiRegionView::move_selection(double dx
, double dy
)
1912 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1913 (*i
)->move_event(dx
, dy
);
1918 MidiRegionView::note_dropped(CanvasNoteEvent
*, double dt
, int8_t dnote
)
1920 assert (!_selection
.empty());
1922 uint8_t lowest_note_in_selection
= 127;
1923 uint8_t highest_note_in_selection
= 0;
1924 uint8_t highest_note_difference
= 0;
1926 // find highest and lowest notes first
1928 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1929 uint8_t pitch
= (*i
)->note()->note();
1930 lowest_note_in_selection
= std::min(lowest_note_in_selection
, pitch
);
1931 highest_note_in_selection
= std::max(highest_note_in_selection
, pitch
);
1935 cerr << "dnote: " << (int) dnote << endl;
1936 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
1937 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
1938 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
1939 << int(highest_note_in_selection) << endl;
1940 cerr << "selection size: " << _selection.size() << endl;
1941 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
1944 // Make sure the note pitch does not exceed the MIDI standard range
1945 if (highest_note_in_selection
+ dnote
> 127) {
1946 highest_note_difference
= highest_note_in_selection
- 127;
1949 start_diff_command(_("move notes"));
1951 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end() ; ++i
) {
1953 nframes64_t start_frames
= beats_to_frames((*i
)->note()->time());
1956 start_frames
+= snap_frame_to_frame(trackview
.editor().pixel_to_frame(dt
));
1958 start_frames
-= snap_frame_to_frame(trackview
.editor().pixel_to_frame(-dt
));
1961 Evoral::MusicalTime new_time
= frames_to_beats(start_frames
);
1967 diff_add_change (*i
, MidiModel::DiffCommand::StartTime
, new_time
);
1969 uint8_t original_pitch
= (*i
)->note()->note();
1970 uint8_t new_pitch
= original_pitch
+ dnote
- highest_note_difference
;
1972 // keep notes in standard midi range
1973 clamp_to_0_127(new_pitch
);
1975 // keep original pitch if note is dragged outside valid midi range
1976 if ((original_pitch
!= 0 && new_pitch
== 0)
1977 || (original_pitch
!= 127 && new_pitch
== 127)) {
1978 new_pitch
= original_pitch
;
1981 lowest_note_in_selection
= std::min(lowest_note_in_selection
, new_pitch
);
1982 highest_note_in_selection
= std::max(highest_note_in_selection
, new_pitch
);
1984 diff_add_change (*i
, MidiModel::DiffCommand::NoteNumber
, new_pitch
);
1989 // care about notes being moved beyond the upper/lower bounds on the canvas
1990 if (lowest_note_in_selection
< midi_stream_view()->lowest_note() ||
1991 highest_note_in_selection
> midi_stream_view()->highest_note()) {
1992 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange
);
1997 MidiRegionView::snap_pixel_to_frame(double x
)
1999 PublicEditor
& editor
= trackview
.editor();
2000 // x is region relative, convert it to global absolute frames
2001 nframes64_t frame
= editor
.pixel_to_frame(x
) + _region
->position();
2002 editor
.snap_to(frame
);
2003 return frame
- _region
->position(); // convert back to region relative
2007 MidiRegionView::snap_frame_to_frame(nframes64_t x
)
2009 PublicEditor
& editor
= trackview
.editor();
2010 // x is region relative, convert it to global absolute frames
2011 nframes64_t frame
= x
+ _region
->position();
2012 editor
.snap_to(frame
);
2013 return frame
- _region
->position(); // convert back to region relative
2017 MidiRegionView::snap_to_pixel(double x
)
2019 return (double) trackview
.editor().frame_to_pixel(snap_pixel_to_frame(x
));
2023 MidiRegionView::get_position_pixels()
2025 nframes64_t region_frame
= get_position();
2026 return trackview
.editor().frame_to_pixel(region_frame
);
2030 MidiRegionView::get_end_position_pixels()
2032 nframes64_t frame
= get_position() + get_duration ();
2033 return trackview
.editor().frame_to_pixel(frame
);
2037 MidiRegionView::beats_to_frames(double beats
) const
2039 return _time_converter
.to(beats
);
2043 MidiRegionView::frames_to_beats(nframes64_t frames
) const
2045 return _time_converter
.from(frames
);
2049 MidiRegionView::begin_resizing (bool /*at_front*/)
2051 _resize_data
.clear();
2053 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2054 CanvasNote
*note
= dynamic_cast<CanvasNote
*> (*i
);
2056 // only insert CanvasNotes into the map
2058 NoteResizeData
*resize_data
= new NoteResizeData();
2059 resize_data
->canvas_note
= note
;
2061 // create a new SimpleRect from the note which will be the resize preview
2062 SimpleRect
*resize_rect
= new SimpleRect(
2063 *group
, note
->x1(), note
->y1(), note
->x2(), note
->y2());
2065 // calculate the colors: get the color settings
2066 uint32_t fill_color
= UINT_RGBA_CHANGE_A(
2067 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get(),
2070 // make the resize preview notes more transparent and bright
2071 fill_color
= UINT_INTERPOLATE(fill_color
, 0xFFFFFF40, 0.5);
2073 // calculate color based on note velocity
2074 resize_rect
->property_fill_color_rgba() = UINT_INTERPOLATE(
2075 CanvasNoteEvent::meter_style_fill_color(note
->note()->velocity()),
2079 resize_rect
->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2080 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get());
2082 resize_data
->resize_rect
= resize_rect
;
2083 _resize_data
.push_back(resize_data
);
2088 /** Update resizing notes while user drags.
2089 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2090 * @param at_front which end of the note (true == note on, false == note off)
2091 * @param delta_x change in mouse position since the start of the drag
2092 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2093 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2094 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2095 * as the \a primary note.
2098 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote
* primary
, bool at_front
, double delta_x
, bool relative
)
2100 bool cursor_set
= false;
2102 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2103 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2104 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2109 current_x
= canvas_note
->x1() + delta_x
;
2111 current_x
= primary
->x1() + delta_x
;
2115 current_x
= canvas_note
->x2() + delta_x
;
2117 current_x
= primary
->x2() + delta_x
;
2122 resize_rect
->property_x1() = snap_to_pixel(current_x
);
2123 resize_rect
->property_x2() = canvas_note
->x2();
2125 resize_rect
->property_x2() = snap_to_pixel(current_x
);
2126 resize_rect
->property_x1() = canvas_note
->x1();
2132 beats
= snap_pixel_to_frame (current_x
);
2133 beats
= frames_to_beats (beats
);
2138 if (beats
< canvas_note
->note()->end_time()) {
2139 len
= canvas_note
->note()->time() - beats
;
2140 len
+= canvas_note
->note()->length();
2145 if (beats
>= canvas_note
->note()->end_time()) {
2146 len
= beats
- canvas_note
->note()->time();
2153 snprintf (buf
, sizeof (buf
), "%.3g beats", len
);
2154 trackview
.editor().show_verbose_canvas_cursor_with (buf
);
2163 /** Finish resizing notes when the user releases the mouse button.
2164 * Parameters the same as for \a update_resizing().
2167 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote
* primary
, bool at_front
, double delta_x
, bool relative
)
2169 start_diff_command(_("resize notes"));
2171 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2172 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2173 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2178 current_x
= canvas_note
->x1() + delta_x
;
2180 current_x
= primary
->x1() + delta_x
;
2184 current_x
= canvas_note
->x2() + delta_x
;
2186 current_x
= primary
->x2() + delta_x
;
2190 current_x
= snap_pixel_to_frame (current_x
);
2191 current_x
= frames_to_beats (current_x
);
2193 if (at_front
&& current_x
< canvas_note
->note()->end_time()) {
2194 diff_add_change (canvas_note
, MidiModel::DiffCommand::StartTime
, current_x
);
2196 double len
= canvas_note
->note()->time() - current_x
;
2197 len
+= canvas_note
->note()->length();
2200 /* XXX convert to beats */
2201 diff_add_change (canvas_note
, MidiModel::DiffCommand::Length
, len
);
2206 double len
= current_x
- canvas_note
->note()->time();
2209 /* XXX convert to beats */
2210 diff_add_change (canvas_note
, MidiModel::DiffCommand::Length
, len
);
2218 _resize_data
.clear();
2223 MidiRegionView::change_note_velocity(CanvasNoteEvent
* event
, int8_t velocity
, bool relative
)
2225 uint8_t new_velocity
;
2228 new_velocity
= event
->note()->velocity() + velocity
;
2229 clamp_to_0_127(new_velocity
);
2231 new_velocity
= velocity
;
2234 // event->show_velocity ();
2236 diff_add_change (event
, MidiModel::DiffCommand::Velocity
, new_velocity
);
2240 MidiRegionView::change_note_note (CanvasNoteEvent
* event
, int8_t note
, bool relative
)
2245 new_note
= event
->note()->note() + note
;
2250 clamp_to_0_127 (new_note
);
2251 diff_add_change (event
, MidiModel::DiffCommand::NoteNumber
, new_note
);
2255 MidiRegionView::trim_note (CanvasNoteEvent
* event
, Evoral::MusicalTime front_delta
, Evoral::MusicalTime end_delta
)
2257 bool change_start
= false;
2258 bool change_length
= false;
2259 Evoral::MusicalTime new_start
= 0;
2260 Evoral::MusicalTime new_length
= 0;
2262 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2264 front_delta: if positive - move the start of the note later in time (shortening it)
2265 if negative - move the start of the note earlier in time (lengthening it)
2267 end_delta: if positive - move the end of the note later in time (lengthening it)
2268 if negative - move the end of the note earlier in time (shortening it)
2272 if (front_delta
< 0) {
2274 if (event
->note()->time() < -front_delta
) {
2277 new_start
= event
->note()->time() + front_delta
; // moves earlier
2280 /* start moved toward zero, so move the end point out to where it used to be.
2281 Note that front_delta is negative, so this increases the length.
2284 new_length
= event
->note()->length() - front_delta
;
2285 change_start
= true;
2286 change_length
= true;
2290 Evoral::MusicalTime new_pos
= event
->note()->time() + front_delta
;
2292 if (new_pos
< event
->note()->end_time()) {
2293 new_start
= event
->note()->time() + front_delta
;
2294 /* start moved toward the end, so move the end point back to where it used to be */
2295 new_length
= event
->note()->length() - front_delta
;
2296 change_start
= true;
2297 change_length
= true;
2304 bool can_change
= true;
2305 if (end_delta
< 0) {
2306 if (event
->note()->length() < -end_delta
) {
2312 new_length
= event
->note()->length() + end_delta
;
2313 change_length
= true;
2318 diff_add_change (event
, MidiModel::DiffCommand::StartTime
, new_start
);
2321 if (change_length
) {
2322 diff_add_change (event
, MidiModel::DiffCommand::Length
, new_length
);
2327 MidiRegionView::change_note_time (CanvasNoteEvent
* event
, Evoral::MusicalTime delta
, bool relative
)
2329 Evoral::MusicalTime new_time
;
2333 if (event
->note()->time() < -delta
) {
2336 new_time
= event
->note()->time() + delta
;
2339 new_time
= event
->note()->time() + delta
;
2345 diff_add_change (event
, MidiModel::DiffCommand::StartTime
, new_time
);
2349 MidiRegionView::change_velocities (bool up
, bool fine
, bool allow_smush
)
2353 if (_selection
.empty()) {
2368 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2369 if ((*i
)->note()->velocity() + delta
== 0 || (*i
)->note()->velocity() + delta
== 127) {
2375 start_diff_command(_("change velocities"));
2377 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end();) {
2378 Selection::iterator next
= i
;
2380 change_note_velocity (*i
, delta
, true);
2384 if (!_selection
.empty()) {
2386 snprintf (buf
, sizeof (buf
), "Vel %d",
2387 (int) (*_selection
.begin())->note()->velocity());
2388 trackview
.editor().show_verbose_canvas_cursor_with (buf
);
2396 MidiRegionView::transpose (bool up
, bool fine
, bool allow_smush
)
2398 if (_selection
.empty()) {
2415 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2417 if ((int8_t) (*i
)->note()->note() + delta
<= 0) {
2421 if ((int8_t) (*i
)->note()->note() + delta
> 127) {
2428 start_diff_command (_("transpose"));
2430 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2431 Selection::iterator next
= i
;
2433 change_note_note (*i
, delta
, true);
2441 MidiRegionView::change_note_lengths (bool fine
, bool shorter
, bool start
, bool end
)
2443 Evoral::MusicalTime delta
;
2448 /* grab the current grid distance */
2450 delta
= trackview
.editor().get_grid_type_as_beats (success
, _region
->position());
2452 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2453 cerr
<< "Grid type not available as beats - TO BE FIXED\n";
2462 start_diff_command (_("change note lengths"));
2464 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2465 Selection::iterator next
= i
;
2468 /* note the negation of the delta for start */
2470 trim_note (*i
, (start
? -delta
: 0), (end
? delta
: 0));
2479 MidiRegionView::nudge_notes (bool forward
)
2481 if (_selection
.empty()) {
2485 /* pick a note as the point along the timeline to get the nudge distance.
2486 its not necessarily the earliest note, so we may want to pull the notes out
2487 into a vector and sort before using the first one.
2490 nframes64_t ref_point
= _region
->position() + beats_to_frames ((*(_selection
.begin()))->note()->time());
2492 nframes64_t distance
;
2494 if (trackview
.editor().snap_mode() == Editing::SnapOff
) {
2496 /* grid is off - use nudge distance */
2498 distance
= trackview
.editor().get_nudge_distance (ref_point
, unused
);
2504 nframes64_t next_pos
= ref_point
;
2507 /* XXX need check on max_frames, but that needs max_frames64 or something */
2510 if (next_pos
== 0) {
2516 trackview
.editor().snap_to (next_pos
, (forward
? 1 : -1), false);
2517 distance
= ref_point
- next_pos
;
2520 if (distance
== 0) {
2524 Evoral::MusicalTime delta
= frames_to_beats (fabs (distance
));
2530 start_diff_command (_("nudge"));
2532 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2533 Selection::iterator next
= i
;
2535 change_note_time (*i
, delta
, true);
2543 MidiRegionView::change_channel(uint8_t channel
)
2545 start_diff_command(_("change channel"));
2546 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2547 diff_add_change (*i
, MidiModel::DiffCommand::Channel
, channel
);
2555 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent
* ev
)
2557 if (_mouse_state
== SelectTouchDragging
) {
2558 note_selected (ev
, true);
2561 show_verbose_canvas_cursor (ev
->note ());
2565 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent
* note
)
2567 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2568 (*i
)->hide_velocity ();
2571 trackview
.editor().hide_verbose_canvas_cursor ();
2575 MidiRegionView::switch_source(boost::shared_ptr
<Source
> src
)
2577 boost::shared_ptr
<MidiSource
> msrc
= boost::dynamic_pointer_cast
<MidiSource
>(src
);
2579 display_model(msrc
->model());
2583 MidiRegionView::set_frame_color()
2586 if (_selected
&& should_show_selection
) {
2587 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase
.get();
2589 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase
.get();
2595 MidiRegionView::midi_channel_mode_changed(ChannelMode mode
, uint16_t mask
)
2599 case FilterChannels
:
2600 _force_channel
= -1;
2603 _force_channel
= mask
;
2604 mask
= 0xFFFF; // Show all notes as active (below)
2607 // Update notes for selection
2608 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2609 (*i
)->on_channel_selection_change(mask
);
2612 _last_channel_selection
= mask
;
2616 MidiRegionView::midi_patch_settings_changed(std::string model
, std::string custom_device_mode
)
2618 _model_name
= model
;
2619 _custom_device_mode
= custom_device_mode
;
2624 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op
)
2626 if (_selection
.empty()) {
2630 PublicEditor
& editor (trackview
.editor());
2635 editor
.get_cut_buffer().add (selection_as_cut_buffer());
2643 start_diff_command();
2645 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2651 diff_remove_note (*i
);
2661 MidiRegionView::selection_as_cut_buffer () const
2665 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2666 NoteType
* n
= (*i
)->note().get();
2667 notes
.insert (boost::shared_ptr
<NoteType
> (new NoteType (*n
)));
2670 MidiCutBuffer
* cb
= new MidiCutBuffer (trackview
.session());
2677 MidiRegionView::paste (nframes64_t pos
, float times
, const MidiCutBuffer
& mcb
)
2683 start_diff_command (_("paste"));
2685 Evoral::MusicalTime beat_delta
;
2686 Evoral::MusicalTime paste_pos_beats
;
2687 Evoral::MusicalTime duration
;
2688 Evoral::MusicalTime end_point
= 0;
2690 duration
= (*mcb
.notes().rbegin())->end_time() - (*mcb
.notes().begin())->time();
2691 paste_pos_beats
= frames_to_beats (pos
- _region
->position());
2692 beat_delta
= (*mcb
.notes().begin())->time() - paste_pos_beats
;
2693 paste_pos_beats
= 0;
2695 _selection
.clear ();
2697 for (int n
= 0; n
< (int) times
; ++n
) {
2699 for (Notes::const_iterator i
= mcb
.notes().begin(); i
!= mcb
.notes().end(); ++i
) {
2701 boost::shared_ptr
<NoteType
> copied_note (new NoteType (*((*i
).get())));
2702 copied_note
->set_time (paste_pos_beats
+ copied_note
->time() - beat_delta
);
2704 /* make all newly added notes selected */
2706 diff_add_note (copied_note
, true);
2707 end_point
= copied_note
->end_time();
2710 paste_pos_beats
+= duration
;
2713 /* if we pasted past the current end of the region, extend the region */
2715 nframes64_t end_frame
= _region
->position() + beats_to_frames (end_point
);
2716 nframes64_t region_end
= _region
->position() + _region
->length() - 1;
2718 if (end_frame
> region_end
) {
2720 trackview
.session()->begin_reversible_command (_("paste"));
2722 _region
->clear_history ();
2723 _region
->set_length (end_frame
, this);
2724 trackview
.session()->add_command (new StatefulDiffCommand (_region
));
2730 struct EventNoteTimeEarlyFirstComparator
{
2731 bool operator() (CanvasNoteEvent
* a
, CanvasNoteEvent
* b
) {
2732 return a
->note()->time() < b
->note()->time();
2737 MidiRegionView::time_sort_events ()
2739 if (!_sort_needed
) {
2743 EventNoteTimeEarlyFirstComparator cmp
;
2746 _sort_needed
= false;
2750 MidiRegionView::goto_next_note ()
2752 // nframes64_t pos = -1;
2753 bool use_next
= false;
2755 if (_events
.back()->selected()) {
2759 time_sort_events ();
2761 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2762 if ((*i
)->selected()) {
2765 } else if (use_next
) {
2767 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2772 /* use the first one */
2774 unique_select (_events
.front());
2779 MidiRegionView::goto_previous_note ()
2781 // nframes64_t pos = -1;
2782 bool use_next
= false;
2784 if (_events
.front()->selected()) {
2788 time_sort_events ();
2790 for (Events::reverse_iterator i
= _events
.rbegin(); i
!= _events
.rend(); ++i
) {
2791 if ((*i
)->selected()) {
2794 } else if (use_next
) {
2796 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2801 /* use the last one */
2803 unique_select (*(_events
.rbegin()));
2807 MidiRegionView::selection_as_notelist (Notes
& selected
, bool allow_all_if_none_selected
)
2809 bool had_selected
= false;
2811 time_sort_events ();
2813 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2814 if ((*i
)->selected()) {
2815 selected
.insert ((*i
)->note());
2816 had_selected
= true;
2820 if (allow_all_if_none_selected
&& !had_selected
) {
2821 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2822 selected
.insert ((*i
)->note());
2828 MidiRegionView::update_ghost_note (double x
, double y
)
2834 nframes64_t f
= trackview
.editor().pixel_to_frame (x
) + _region
->position ();
2835 trackview
.editor().snap_to (f
);
2836 f
-= _region
->position ();
2839 Evoral::MusicalTime beats
= trackview
.editor().get_grid_type_as_beats (success
, f
);
2844 double length
= frames_to_beats (snap_frame_to_frame (f
+ beats_to_frames (beats
)) - f
);
2846 _ghost_note
->note()->set_time (frames_to_beats (f
+ _region
->start()));
2847 _ghost_note
->note()->set_length (length
);
2848 _ghost_note
->note()->set_note (midi_stream_view()->y_to_note (y
));
2850 update_note (_ghost_note
);
2852 show_verbose_canvas_cursor (_ghost_note
->note ());
2856 MidiRegionView::create_ghost_note (double x
, double y
)
2861 boost::shared_ptr
<NoteType
> g (new NoteType
);
2862 _ghost_note
= new NoEventCanvasNote (*this, *group
, g
);
2863 update_ghost_note (x
, y
);
2864 _ghost_note
->show ();
2869 show_verbose_canvas_cursor (_ghost_note
->note ());
2873 MidiRegionView::snap_changed ()
2879 create_ghost_note (_last_ghost_x
, _last_ghost_y
);
2883 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr
<NoteType
> n
) const
2886 snprintf (buf
, sizeof (buf
), "%s (%d)\nVel %d",
2887 Evoral::midi_note_name (n
->note()).c_str(),
2889 (int) n
->velocity());
2890 trackview
.editor().show_verbose_canvas_cursor_with (buf
);
2894 MidiRegionView::drop_down_keys ()
2896 _mouse_state
= None
;
2900 MidiRegionView::maybe_select_by_position (GdkEventButton
* ev
, double x
, double y
)
2902 double note
= midi_stream_view()->y_to_note(y
);
2904 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
2906 cerr
<< "Selecting by position\n";
2908 uint16_t chn_mask
= mtv
->channel_selector().get_selected_channels();
2910 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::TertiaryModifier
)) {
2911 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchGreaterThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
2912 } else if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
2913 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchLessThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
2918 bool add_mrv_selection
= false;
2920 if (_selection
.empty()) {
2921 add_mrv_selection
= true;
2924 for (Events::iterator i
= e
.begin(); i
!= e
.end(); ++i
) {
2925 if (_selection
.insert (*i
).second
) {
2926 (*i
)->selected (true);
2930 if (add_mrv_selection
) {
2931 PublicEditor
& editor (trackview
.editor());
2932 editor
.get_selection().add (this);