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 /* since GTK bindings are generally activated on press, and since
542 detectable auto-repeat is the name of the game and only sends
543 repeated presses, carry out key actions at key press, not release.
546 if (ev
->keyval
== GDK_Alt_L
|| ev
->keyval
== GDK_Alt_R
){
547 _mouse_state
= SelectTouchDragging
;
550 } else if (ev
->keyval
== GDK_Escape
) {
554 } else if (ev
->keyval
== GDK_comma
|| ev
->keyval
== GDK_period
) {
556 bool start
= (ev
->keyval
== GDK_comma
);
557 bool end
= (ev
->keyval
== GDK_period
);
558 bool shorter
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
);
559 bool fine
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
561 change_note_lengths (fine
, shorter
, start
, end
);
565 } else if (ev
->keyval
== GDK_Delete
) {
570 } else if (ev
->keyval
== GDK_Tab
) {
572 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
573 goto_previous_note ();
579 } else if (ev
->keyval
== GDK_Up
) {
581 bool allow_smush
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::TertiaryModifier
);
582 bool fine
= !Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
584 if (Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
)) {
585 change_velocities (true, fine
, allow_smush
);
587 transpose (true, fine
, allow_smush
);
591 } else if (ev
->keyval
== GDK_Down
) {
593 bool allow_smush
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::TertiaryModifier
);
594 bool fine
= !Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
596 if (Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
)) {
597 change_velocities (false, fine
, allow_smush
);
599 transpose (false, fine
, allow_smush
);
603 } else if (ev
->keyval
== GDK_Left
) {
608 } else if (ev
->keyval
== GDK_Right
) {
613 } else if (ev
->keyval
== GDK_Control_L
) {
616 } else if (ev
->keyval
== GDK_r
) {
617 /* yes, this steals r */
618 if (midi_view()->midi_track()->step_editing()) {
619 midi_view()->step_edit_rest ();
628 MidiRegionView::key_release (GdkEventKey
* ev
)
630 if (ev
->keyval
== GDK_Alt_L
|| ev
->keyval
== GDK_Alt_R
) {
638 MidiRegionView::show_list_editor ()
641 _list_editor
= new MidiListEditor (trackview
.session(), midi_region());
643 _list_editor
->present ();
646 /** Add a note to the model, and the view, at a canvas (click) coordinate.
647 * \param x horizontal position in pixels
648 * \param y vertical position in pixels
649 * \param length duration of the note in beats, which will be snapped to the grid
650 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
653 MidiRegionView::create_note_at(double x
, double y
, double length
, bool sh
)
655 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
656 MidiStreamView
* const view
= mtv
->midi_view();
658 double note
= midi_stream_view()->y_to_note(y
);
661 assert(note
<= 127.0);
663 // Start of note in frames relative to region start
664 nframes64_t
const start_frames
= snap_frame_to_frame(trackview
.editor().pixel_to_frame(x
));
665 assert(start_frames
>= 0);
668 length
= frames_to_beats(
669 snap_frame_to_frame(start_frames
+ beats_to_frames(length
)) - start_frames
);
671 assert (length
!= 0);
674 length
= frames_to_beats (beats_to_frames (length
) - 1);
677 uint16_t chn_mask
= mtv
->channel_selector().get_selected_channels();
681 /* pick the highest selected channel, unless all channels are selected,
682 which is interpreted to mean channel 1 (zero)
685 for (uint16_t i
= 0; i
< 16; ++i
) {
686 if (chn_mask
& (1<<i
)) {
696 const boost::shared_ptr
<NoteType
> new_note (new NoteType (channel
,
697 frames_to_beats(start_frames
+ _region
->start()), length
,
698 (uint8_t)note
, 0x40));
700 if (_model
->contains (new_note
)) {
704 view
->update_note_range(new_note
->note());
706 MidiModel::DiffCommand
* cmd
= _model
->new_diff_command("add note");
708 _model
->apply_command(*trackview
.session(), cmd
);
710 play_midi_note (new_note
);
714 MidiRegionView::clear_events()
719 for (std::vector
<GhostRegion
*>::iterator g
= ghosts
.begin(); g
!= ghosts
.end(); ++g
) {
720 if ((gr
= dynamic_cast<MidiGhostRegion
*>(*g
)) != 0) {
725 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
730 _pgm_changes
.clear();
732 _optimization_iterator
= _events
.end();
737 MidiRegionView::display_model(boost::shared_ptr
<MidiModel
> model
)
740 content_connection
.disconnect ();
741 _model
->ContentsChanged
.connect (content_connection
, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model
, this), gui_context());
745 if (_enable_display
) {
751 MidiRegionView::start_diff_command(string name
)
753 if (!_diff_command
) {
754 _diff_command
= _model
->new_diff_command(name
);
759 MidiRegionView::diff_add_note(const boost::shared_ptr
<NoteType
> note
, bool selected
, bool show_velocity
)
762 _diff_command
->add(note
);
765 _marked_for_selection
.insert(note
);
768 _marked_for_velocity
.insert(note
);
773 MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent
* ev
)
775 if (_diff_command
&& ev
->note()) {
776 _diff_command
->remove(ev
->note());
781 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent
* ev
,
782 MidiModel::DiffCommand::Property property
,
786 _diff_command
->change (ev
->note(), property
, val
);
791 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent
* ev
,
792 MidiModel::DiffCommand::Property property
,
793 Evoral::MusicalTime val
)
796 _diff_command
->change (ev
->note(), property
, val
);
801 MidiRegionView::apply_diff ()
805 if (!_diff_command
) {
809 if ((add_or_remove
= _diff_command
->adds_or_removes())) {
810 // Mark all selected notes for selection when model reloads
811 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
812 _marked_for_selection
.insert((*i
)->note());
816 _model
->apply_command(*trackview
.session(), _diff_command
);
818 midi_view()->midi_track()->playlist_modified();
822 _marked_for_selection
.clear();
825 _marked_for_velocity
.clear();
829 MidiRegionView::apply_diff_as_subcommand()
833 if (!_diff_command
) {
837 if ((add_or_remove
= _diff_command
->adds_or_removes())) {
838 // Mark all selected notes for selection when model reloads
839 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
840 _marked_for_selection
.insert((*i
)->note());
844 _model
->apply_command_as_subcommand(*trackview
.session(), _diff_command
);
846 midi_view()->midi_track()->playlist_modified();
849 _marked_for_selection
.clear();
851 _marked_for_velocity
.clear();
856 MidiRegionView::abort_command()
858 delete _diff_command
;
864 MidiRegionView::find_canvas_note (boost::shared_ptr
<NoteType
> note
)
866 if (_optimization_iterator
!= _events
.end()) {
867 ++_optimization_iterator
;
870 if (_optimization_iterator
!= _events
.end() && (*_optimization_iterator
)->note() == note
) {
871 return *_optimization_iterator
;
874 for (_optimization_iterator
= _events
.begin(); _optimization_iterator
!= _events
.end(); ++_optimization_iterator
) {
875 if ((*_optimization_iterator
)->note() == note
) {
876 return *_optimization_iterator
;
884 MidiRegionView::get_events (Events
& e
, Evoral::Sequence
<Evoral::MusicalTime
>::NoteOperator op
, uint8_t val
, int chan_mask
)
886 MidiModel::Notes notes
;
887 _model
->get_notes (notes
, op
, val
, chan_mask
);
889 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
890 CanvasNoteEvent
* cne
= find_canvas_note (*n
);
898 MidiRegionView::redisplay_model()
900 // Don't redisplay the model if we're currently recording and displaying that
906 cerr
<< "MidiRegionView::redisplay_model called without a model" << endmsg
;
910 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
914 MidiModel::ReadLock
lock(_model
->read_lock());
916 MidiModel::Notes
& notes (_model
->notes());
917 _optimization_iterator
= _events
.begin();
919 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
921 boost::shared_ptr
<NoteType
> note (*n
);
922 CanvasNoteEvent
* cne
;
925 if (note_in_region_range (note
, visible
)) {
927 if ((cne
= find_canvas_note (note
)) != 0) {
934 if ((cn
= dynamic_cast<CanvasNote
*>(cne
)) != 0) {
936 } else if ((ch
= dynamic_cast<CanvasHit
*>(cne
)) != 0) {
948 add_note (note
, visible
);
953 if ((cne
= find_canvas_note (note
)) != 0) {
961 /* remove note items that are no longer valid */
963 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ) {
964 if (!(*i
)->valid ()) {
966 i
= _events
.erase (i
);
972 _pgm_changes
.clear();
976 display_program_changes();
978 _marked_for_selection
.clear ();
979 _marked_for_velocity
.clear ();
981 /* we may have caused _events to contain things out of order (e.g. if a note
982 moved earlier or later). we don't generally need them in time order, but
983 make a note that a sort is required for those cases that require it.
990 MidiRegionView::display_program_changes()
992 boost::shared_ptr
<Evoral::Control
> control
= _model
->control(MidiPgmChangeAutomation
);
997 Glib::Mutex::Lock
lock (control
->list()->lock());
999 uint8_t channel
= control
->parameter().channel();
1001 for (AutomationList::const_iterator event
= control
->list()->begin();
1002 event
!= control
->list()->end(); ++event
) {
1003 double event_time
= (*event
)->when
;
1004 double program_number
= floor((*event
)->value
+ 0.5);
1006 // Get current value of bank select MSB at time of the program change
1007 Evoral::Parameter
bank_select_msb(MidiCCAutomation
, channel
, MIDI_CTL_MSB_BANK
);
1008 boost::shared_ptr
<Evoral::Control
> msb_control
= _model
->control(bank_select_msb
);
1010 if (msb_control
!= 0) {
1011 msb
= uint8_t(floor(msb_control
->get_float(true, event_time
) + 0.5));
1014 // Get current value of bank select LSB at time of the program change
1015 Evoral::Parameter
bank_select_lsb(MidiCCAutomation
, channel
, MIDI_CTL_LSB_BANK
);
1016 boost::shared_ptr
<Evoral::Control
> lsb_control
= _model
->control(bank_select_lsb
);
1018 if (lsb_control
!= 0) {
1019 lsb
= uint8_t(floor(lsb_control
->get_float(true, event_time
) + 0.5));
1022 MIDI::Name::PatchPrimaryKey
patch_key(msb
, lsb
, program_number
);
1024 boost::shared_ptr
<MIDI::Name::Patch
> patch
=
1025 MIDI::Name::MidiPatchManager::instance().find_patch(
1026 _model_name
, _custom_device_mode
, channel
, patch_key
);
1028 PCEvent
program_change(event_time
, uint8_t(program_number
), channel
);
1031 add_pgm_change(program_change
, patch
->name());
1034 snprintf(buf
, 4, "%d", int(program_number
));
1035 add_pgm_change(program_change
, buf
);
1041 MidiRegionView::display_sysexes()
1043 for (MidiModel::SysExes::const_iterator i
= _model
->sysexes().begin(); i
!= _model
->sysexes().end(); ++i
) {
1044 Evoral::MusicalTime time
= (*i
)->time();
1049 for (uint32_t b
= 0; b
< (*i
)->size(); ++b
) {
1050 str
<< int((*i
)->buffer()[b
]);
1051 if (b
!= (*i
)->size() -1) {
1055 string text
= str
.str();
1057 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
1059 const double x
= trackview
.editor().frame_to_pixel(beats_to_frames(time
));
1061 double height
= midi_stream_view()->contents_height();
1063 boost::shared_ptr
<CanvasSysEx
> sysex
= boost::shared_ptr
<CanvasSysEx
>(
1064 new CanvasSysEx(*this, *group
, text
, height
, x
, 1.0));
1066 // Show unless program change is beyond the region bounds
1067 if (time
- _region
->start() >= _region
->length() || time
< _region
->start()) {
1073 _sys_exes
.push_back(sysex
);
1078 MidiRegionView::~MidiRegionView ()
1080 in_destructor
= true;
1082 trackview
.editor().hide_verbose_canvas_cursor ();
1084 note_delete_connection
.disconnect ();
1086 delete _list_editor
;
1088 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1090 if (_active_notes
) {
1097 delete _diff_command
;
1101 MidiRegionView::region_resized (const PropertyChange
& what_changed
)
1103 RegionView::region_resized(what_changed
);
1105 if (what_changed
.contains (ARDOUR::Properties::position
)) {
1106 set_duration(_region
->length(), 0);
1107 if (_enable_display
) {
1114 MidiRegionView::reset_width_dependent_items (double pixel_width
)
1116 RegionView::reset_width_dependent_items(pixel_width
);
1117 assert(_pixel_width
== pixel_width
);
1119 if (_enable_display
) {
1125 MidiRegionView::set_height (double height
)
1127 static const double FUDGE
= 2.0;
1128 const double old_height
= _height
;
1129 RegionView::set_height(height
);
1130 _height
= height
- FUDGE
;
1132 apply_note_range(midi_stream_view()->lowest_note(),
1133 midi_stream_view()->highest_note(),
1134 height
!= old_height
+ FUDGE
);
1137 name_pixbuf
->raise_to_top();
1142 /** Apply the current note range from the stream view
1143 * by repositioning/hiding notes as necessary
1146 MidiRegionView::apply_note_range (uint8_t min
, uint8_t max
, bool force
)
1148 if (!_enable_display
) {
1152 if (!force
&& _current_range_min
== min
&& _current_range_max
== max
) {
1156 _current_range_min
= min
;
1157 _current_range_max
= max
;
1159 for (Events::const_iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1160 CanvasNoteEvent
* event
= *i
;
1161 boost::shared_ptr
<NoteType
> note (event
->note());
1163 if (note
->note() < _current_range_min
||
1164 note
->note() > _current_range_max
) {
1170 if (CanvasNote
* cnote
= dynamic_cast<CanvasNote
*>(event
)) {
1172 const double y1
= midi_stream_view()->note_to_y(note
->note());
1173 const double y2
= y1
+ floor(midi_stream_view()->note_height());
1175 cnote
->property_y1() = y1
;
1176 cnote
->property_y2() = y2
;
1178 } else if (CanvasHit
* chit
= dynamic_cast<CanvasHit
*>(event
)) {
1180 double x
= trackview
.editor().frame_to_pixel(
1181 beats_to_frames(note
->time()) - _region
->start());
1182 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1183 double y
= midi_stream_view()->note_to_y(event
->note()->note())
1184 + ((diamond_size
-2.0) / 4.0);
1186 chit
->set_height (diamond_size
);
1187 chit
->move (x
- chit
->x1(), y
- chit
->y1());
1194 MidiRegionView::add_ghost (TimeAxisView
& tv
)
1198 double unit_position
= _region
->position () / samples_per_unit
;
1199 MidiTimeAxisView
* mtv
= dynamic_cast<MidiTimeAxisView
*>(&tv
);
1200 MidiGhostRegion
* ghost
;
1202 if (mtv
&& mtv
->midi_view()) {
1203 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1204 to allow having midi notes on top of note lines and waveforms.
1206 ghost
= new MidiGhostRegion (*mtv
->midi_view(), trackview
, unit_position
);
1208 ghost
= new MidiGhostRegion (tv
, trackview
, unit_position
);
1211 ghost
->set_height ();
1212 ghost
->set_duration (_region
->length() / samples_per_unit
);
1213 ghosts
.push_back (ghost
);
1215 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1216 if ((note
= dynamic_cast<CanvasNote
*>(*i
)) != 0) {
1217 ghost
->add_note(note
);
1221 GhostRegion::CatchDeletion
.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost
, this, _1
), gui_context());
1227 /** Begin tracking note state for successive calls to add_event
1230 MidiRegionView::begin_write()
1232 assert(!_active_notes
);
1233 _active_notes
= new CanvasNote
*[128];
1234 for (unsigned i
=0; i
< 128; ++i
) {
1235 _active_notes
[i
] = 0;
1240 /** Destroy note state for add_event
1243 MidiRegionView::end_write()
1245 delete[] _active_notes
;
1247 _marked_for_selection
.clear();
1248 _marked_for_velocity
.clear();
1252 /** Resolve an active MIDI note (while recording).
1255 MidiRegionView::resolve_note(uint8_t note
, double end_time
)
1257 if (midi_view()->note_mode() != Sustained
) {
1261 if (_active_notes
&& _active_notes
[note
]) {
1262 const nframes64_t end_time_frames
= beats_to_frames(end_time
);
1263 _active_notes
[note
]->property_x2() = trackview
.editor().frame_to_pixel(end_time_frames
);
1264 _active_notes
[note
]->property_outline_what() = (guint32
) 0xF; // all edges
1265 _active_notes
[note
] = 0;
1270 /** Extend active notes to rightmost edge of region (if length is changed)
1273 MidiRegionView::extend_active_notes()
1275 if (!_active_notes
) {
1279 for (unsigned i
=0; i
< 128; ++i
) {
1280 if (_active_notes
[i
]) {
1281 _active_notes
[i
]->property_x2() = trackview
.editor().frame_to_pixel(_region
->length());
1287 MidiRegionView::play_midi_note(boost::shared_ptr
<NoteType
> note
)
1289 if (no_sound_notes
|| !trackview
.editor().sound_notes()) {
1293 RouteUI
* route_ui
= dynamic_cast<RouteUI
*> (&trackview
);
1296 route_ui
->midi_track()->write_immediate_event(
1297 note
->on_event().size(), note
->on_event().buffer());
1299 const double note_length_beats
= (note
->off_event().time() - note
->on_event().time());
1300 nframes_t note_length_ms
= beats_to_frames(note_length_beats
)
1301 * (1000 / (double)route_ui
->session()->nominal_frame_rate());
1302 Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(this, &MidiRegionView::play_midi_note_off
), note
),
1303 note_length_ms
, G_PRIORITY_DEFAULT
);
1307 MidiRegionView::play_midi_note_off(boost::shared_ptr
<NoteType
> note
)
1309 RouteUI
* route_ui
= dynamic_cast<RouteUI
*> (&trackview
);
1312 route_ui
->midi_track()->write_immediate_event(
1313 note
->off_event().size(), note
->off_event().buffer());
1319 MidiRegionView::note_in_region_range(const boost::shared_ptr
<NoteType
> note
, bool& visible
) const
1321 const nframes64_t note_start_frames
= beats_to_frames(note
->time());
1323 bool outside
= (note_start_frames
- _region
->start() >= _region
->length()) ||
1324 (note_start_frames
< _region
->start());
1326 visible
= (note
->note() >= midi_stream_view()->lowest_note()) &&
1327 (note
->note() <= midi_stream_view()->highest_note());
1333 MidiRegionView::update_note (CanvasNote
* ev
)
1335 boost::shared_ptr
<NoteType
> note
= ev
->note();
1337 const nframes64_t note_start_frames
= beats_to_frames(note
->time());
1339 /* trim note display to not overlap the end of its region */
1340 const nframes64_t note_end_frames
= min (beats_to_frames (note
->end_time()), _region
->start() + _region
->length());
1342 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1343 const double y1
= midi_stream_view()->note_to_y(note
->note());
1344 const double note_endpixel
= trackview
.editor().frame_to_pixel(note_end_frames
- _region
->start());
1346 ev
->property_x1() = x
;
1347 ev
->property_y1() = y1
;
1348 if (note
->length() > 0) {
1349 ev
->property_x2() = note_endpixel
;
1351 ev
->property_x2() = trackview
.editor().frame_to_pixel(_region
->length());
1353 ev
->property_y2() = y1
+ floor(midi_stream_view()->note_height());
1355 if (note
->length() == 0) {
1356 if (_active_notes
) {
1357 assert(note
->note() < 128);
1358 // If this note is already active there's a stuck note,
1359 // finish the old note rectangle
1360 if (_active_notes
[note
->note()]) {
1361 CanvasNote
* const old_rect
= _active_notes
[note
->note()];
1362 boost::shared_ptr
<NoteType
> old_note
= old_rect
->note();
1363 old_rect
->property_x2() = x
;
1364 old_rect
->property_outline_what() = (guint32
) 0xF;
1366 _active_notes
[note
->note()] = ev
;
1368 /* outline all but right edge */
1369 ev
->property_outline_what() = (guint32
) (0x1 & 0x4 & 0x8);
1371 /* outline all edges */
1372 ev
->property_outline_what() = (guint32
) 0xF;
1377 MidiRegionView::update_hit (CanvasHit
* ev
)
1379 boost::shared_ptr
<NoteType
> note
= ev
->note();
1381 const nframes64_t note_start_frames
= beats_to_frames(note
->time());
1382 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1383 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1384 const double y
= midi_stream_view()->note_to_y(note
->note()) + ((diamond_size
-2) / 4.0);
1389 /** Add a MIDI note to the view (with length).
1391 * If in sustained mode, notes with length 0 will be considered active
1392 * notes, and resolve_note should be called when the corresponding note off
1393 * event arrives, to properly display the note.
1396 MidiRegionView::add_note(const boost::shared_ptr
<NoteType
> note
, bool visible
)
1398 CanvasNoteEvent
* event
= 0;
1400 assert(note
->time() >= 0);
1401 assert(midi_view()->note_mode() == Sustained
|| midi_view()->note_mode() == Percussive
);
1403 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
1405 if (midi_view()->note_mode() == Sustained
) {
1407 CanvasNote
* ev_rect
= new CanvasNote(*this, *group
, note
);
1409 update_note (ev_rect
);
1413 MidiGhostRegion
* gr
;
1415 for (std::vector
<GhostRegion
*>::iterator g
= ghosts
.begin(); g
!= ghosts
.end(); ++g
) {
1416 if ((gr
= dynamic_cast<MidiGhostRegion
*>(*g
)) != 0) {
1417 gr
->add_note(ev_rect
);
1421 } else if (midi_view()->note_mode() == Percussive
) {
1423 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1425 CanvasHit
* ev_diamond
= new CanvasHit(*this, *group
, diamond_size
, note
);
1427 update_hit (ev_diamond
);
1436 if (_marked_for_selection
.find(note
) != _marked_for_selection
.end()) {
1437 note_selected(event
, true);
1440 if (_marked_for_velocity
.find(note
) != _marked_for_velocity
.end()) {
1441 event
->show_velocity();
1443 event
->on_channel_selection_change(_last_channel_selection
);
1444 _events
.push_back(event
);
1455 MidiRegionView::add_note (uint8_t channel
, uint8_t number
, uint8_t velocity
,
1456 Evoral::MusicalTime pos
, Evoral::MusicalTime len
)
1458 boost::shared_ptr
<NoteType
> new_note (new NoteType (channel
, pos
, len
, number
, velocity
));
1460 start_diff_command (_("step add"));
1461 diff_add_note (new_note
, true, false);
1464 /* potentially extend region to hold new note */
1466 nframes64_t end_frame
= _region
->position() + beats_to_frames (new_note
->end_time());
1467 nframes64_t region_end
= _region
->position() + _region
->length() - 1;
1469 if (end_frame
> region_end
) {
1470 _region
->set_length (end_frame
, this);
1477 MidiRegionView::add_pgm_change(PCEvent
& program
, const string
& displaytext
)
1479 assert(program
.time
>= 0);
1481 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
1482 const double x
= trackview
.editor().frame_to_pixel(beats_to_frames(program
.time
));
1484 double height
= midi_stream_view()->contents_height();
1486 boost::shared_ptr
<CanvasProgramChange
> pgm_change
= boost::shared_ptr
<CanvasProgramChange
>(
1487 new CanvasProgramChange(*this, *group
,
1492 _custom_device_mode
,
1493 program
.time
, program
.channel
, program
.value
));
1495 // Show unless program change is beyond the region bounds
1496 if (program
.time
- _region
->start() >= _region
->length() || program
.time
< _region
->start()) {
1502 _pgm_changes
.push_back(pgm_change
);
1506 MidiRegionView::get_patch_key_at(double time
, uint8_t channel
, MIDI::Name::PatchPrimaryKey
& key
)
1508 cerr
<< "getting patch key at " << time
<< " for channel " << channel
<< endl
;
1509 Evoral::Parameter
bank_select_msb(MidiCCAutomation
, channel
, MIDI_CTL_MSB_BANK
);
1510 boost::shared_ptr
<Evoral::Control
> msb_control
= _model
->control(bank_select_msb
);
1512 if (msb_control
!= 0) {
1513 msb
= int(msb_control
->get_float(true, time
));
1514 cerr
<< "got msb " << msb
;
1517 Evoral::Parameter
bank_select_lsb(MidiCCAutomation
, channel
, MIDI_CTL_LSB_BANK
);
1518 boost::shared_ptr
<Evoral::Control
> lsb_control
= _model
->control(bank_select_lsb
);
1520 if (lsb_control
!= 0) {
1521 lsb
= lsb_control
->get_float(true, time
);
1522 cerr
<< " got lsb " << lsb
;
1525 Evoral::Parameter
program_change(MidiPgmChangeAutomation
, channel
, 0);
1526 boost::shared_ptr
<Evoral::Control
> program_control
= _model
->control(program_change
);
1527 float program_number
= -1.0;
1528 if (program_control
!= 0) {
1529 program_number
= program_control
->get_float(true, time
);
1530 cerr
<< " got program " << program_number
<< endl
;
1533 key
.msb
= (int) floor(msb
+ 0.5);
1534 key
.lsb
= (int) floor(lsb
+ 0.5);
1535 key
.program_number
= (int) floor(program_number
+ 0.5);
1536 assert(key
.is_sane());
1541 MidiRegionView::alter_program_change(PCEvent
& old_program
, const MIDI::Name::PatchPrimaryKey
& new_patch
)
1543 // TODO: Get the real event here and alter them at the original times
1544 Evoral::Parameter
bank_select_msb(MidiCCAutomation
, old_program
.channel
, MIDI_CTL_MSB_BANK
);
1545 boost::shared_ptr
<Evoral::Control
> msb_control
= _model
->control(bank_select_msb
);
1546 if (msb_control
!= 0) {
1547 msb_control
->set_float(float(new_patch
.msb
), true, old_program
.time
);
1550 // TODO: Get the real event here and alter them at the original times
1551 Evoral::Parameter
bank_select_lsb(MidiCCAutomation
, old_program
.channel
, MIDI_CTL_LSB_BANK
);
1552 boost::shared_ptr
<Evoral::Control
> lsb_control
= _model
->control(bank_select_lsb
);
1553 if (lsb_control
!= 0) {
1554 lsb_control
->set_float(float(new_patch
.lsb
), true, old_program
.time
);
1557 Evoral::Parameter
program_change(MidiPgmChangeAutomation
, old_program
.channel
, 0);
1558 boost::shared_ptr
<Evoral::Control
> program_control
= _model
->control(program_change
);
1560 assert(program_control
!= 0);
1561 program_control
->set_float(float(new_patch
.program_number
), true, old_program
.time
);
1567 MidiRegionView::program_selected(CanvasProgramChange
& program
, const MIDI::Name::PatchPrimaryKey
& new_patch
)
1569 PCEvent
program_change_event(program
.event_time(), program
.program(), program
.channel());
1570 alter_program_change(program_change_event
, new_patch
);
1574 MidiRegionView::previous_program(CanvasProgramChange
& program
)
1576 MIDI::Name::PatchPrimaryKey key
;
1577 get_patch_key_at(program
.event_time(), program
.channel(), key
);
1579 boost::shared_ptr
<MIDI::Name::Patch
> patch
=
1580 MIDI::Name::MidiPatchManager::instance().previous_patch(
1582 _custom_device_mode
,
1586 PCEvent
program_change_event(program
.event_time(), program
.program(), program
.channel());
1588 alter_program_change(program_change_event
, patch
->patch_primary_key());
1593 MidiRegionView::next_program(CanvasProgramChange
& program
)
1595 MIDI::Name::PatchPrimaryKey key
;
1596 get_patch_key_at(program
.event_time(), program
.channel(), key
);
1598 boost::shared_ptr
<MIDI::Name::Patch
> patch
=
1599 MIDI::Name::MidiPatchManager::instance().next_patch(
1601 _custom_device_mode
,
1605 PCEvent
program_change_event(program
.event_time(), program
.program(), program
.channel());
1607 alter_program_change(program_change_event
, patch
->patch_primary_key());
1612 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent
* cne
)
1614 if (_selection
.empty()) {
1618 if (_selection
.erase (cne
) > 0) {
1619 cerr
<< "Erased a CNE from selection\n";
1624 MidiRegionView::delete_selection()
1626 if (_selection
.empty()) {
1630 start_diff_command (_("delete selection"));
1632 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1633 if ((*i
)->selected()) {
1634 _diff_command
->remove((*i
)->note());
1644 MidiRegionView::delete_note (boost::shared_ptr
<NoteType
> n
)
1646 start_diff_command (_("delete note"));
1647 _diff_command
->remove (n
);
1650 trackview
.editor().hide_verbose_canvas_cursor ();
1654 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent
* ev
)
1656 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1657 if ((*i
)->selected() && (*i
) != ev
) {
1658 (*i
)->set_selected(false);
1659 (*i
)->hide_velocity();
1667 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent
* ev
)
1669 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
1672 Selection::iterator tmp
= i
;
1675 (*i
)->set_selected (false);
1676 _selection
.erase (i
);
1685 /* don't bother with removing this regionview from the editor selection,
1686 since we're about to add another note, and thus put/keep this
1687 regionview in the editor selection.
1690 if (!ev
->selected()) {
1691 add_to_selection (ev
);
1696 MidiRegionView::select_matching_notes (uint8_t notenum
, uint16_t channel_mask
, bool add
, bool extend
)
1698 uint8_t low_note
= 127;
1699 uint8_t high_note
= 0;
1700 MidiModel::Notes
& notes (_model
->notes());
1701 _optimization_iterator
= _events
.begin();
1707 if (extend
&& _selection
.empty()) {
1713 /* scan existing selection to get note range */
1715 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1716 if ((*i
)->note()->note() < low_note
) {
1717 low_note
= (*i
)->note()->note();
1719 if ((*i
)->note()->note() > high_note
) {
1720 high_note
= (*i
)->note()->note();
1724 low_note
= min (low_note
, notenum
);
1725 high_note
= max (high_note
, notenum
);
1728 no_sound_notes
= true;
1730 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1732 boost::shared_ptr
<NoteType
> note (*n
);
1733 CanvasNoteEvent
* cne
;
1734 bool select
= false;
1736 if (((1 << note
->channel()) & channel_mask
) != 0) {
1738 if ((note
->note() >= low_note
&& note
->note() <= high_note
)) {
1741 } else if (note
->note() == notenum
) {
1747 if ((cne
= find_canvas_note (note
)) != 0) {
1748 // extend is false because we've taken care of it,
1749 // since it extends by time range, not pitch.
1750 note_selected (cne
, add
, false);
1754 add
= true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1758 no_sound_notes
= false;
1762 MidiRegionView::toggle_matching_notes (uint8_t notenum
, uint16_t channel_mask
)
1764 MidiModel::Notes
& notes (_model
->notes());
1765 _optimization_iterator
= _events
.begin();
1767 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1769 boost::shared_ptr
<NoteType
> note (*n
);
1770 CanvasNoteEvent
* cne
;
1772 if (note
->note() == notenum
&& (((0x0001 << note
->channel()) & channel_mask
) != 0)) {
1773 if ((cne
= find_canvas_note (note
)) != 0) {
1774 if (cne
->selected()) {
1775 note_deselected (cne
);
1777 note_selected (cne
, true, false);
1785 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent
* ev
, bool add
, bool extend
)
1788 clear_selection_except(ev
);
1793 if (!ev
->selected()) {
1794 add_to_selection (ev
);
1798 /* find end of latest note selected, select all between that and the start of "ev" */
1800 Evoral::MusicalTime earliest
= DBL_MAX
;
1801 Evoral::MusicalTime latest
= 0;
1803 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1804 if ((*i
)->note()->end_time() > latest
) {
1805 latest
= (*i
)->note()->end_time();
1807 if ((*i
)->note()->time() < earliest
) {
1808 earliest
= (*i
)->note()->time();
1812 if (ev
->note()->end_time() > latest
) {
1813 latest
= ev
->note()->end_time();
1816 if (ev
->note()->time() < earliest
) {
1817 earliest
= ev
->note()->time();
1820 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1822 /* find notes entirely within OR spanning the earliest..latest range */
1824 if (((*i
)->note()->time() >= earliest
&& (*i
)->note()->end_time() <= latest
) ||
1825 ((*i
)->note()->time() <= earliest
&& (*i
)->note()->end_time() >= latest
)) {
1826 add_to_selection (*i
);
1830 /* if events were guaranteed to be time sorted, we could do this.
1831 but as of sept 10th 2009, they no longer are.
1834 if ((*i
)->note()->time() > latest
) {
1843 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent
* ev
)
1845 remove_from_selection (ev
);
1849 MidiRegionView::update_drag_selection(double x1
, double x2
, double y1
, double y2
)
1859 // TODO: Make this faster by storing the last updated selection rect, and only
1860 // adjusting things that are in the area that appears/disappeared.
1861 // We probably need a tree to be able to find events in O(log(n)) time.
1863 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1865 /* check if any corner of the note is inside the rect
1868 1) this is computing "touched by", not "contained by" the rect.
1869 2) this does not require that events be sorted in time.
1872 const double ix1
= (*i
)->x1();
1873 const double ix2
= (*i
)->x2();
1874 const double iy1
= (*i
)->y1();
1875 const double iy2
= (*i
)->y2();
1877 if ((ix1
>= x1
&& ix1
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
1878 (ix1
>= x1
&& ix1
<= x2
&& iy2
>= y1
&& iy2
<= y2
) ||
1879 (ix2
>= x1
&& ix2
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
1880 (ix2
>= x1
&& ix2
<= x2
&& iy2
>= y1
&& iy2
<= y2
)) {
1883 if (!(*i
)->selected()) {
1884 add_to_selection (*i
);
1886 } else if ((*i
)->selected()) {
1887 // Not inside rectangle
1888 remove_from_selection (*i
);
1894 MidiRegionView::remove_from_selection (CanvasNoteEvent
* ev
)
1896 Selection::iterator i
= _selection
.find (ev
);
1898 if (i
!= _selection
.end()) {
1899 _selection
.erase (i
);
1902 ev
->set_selected (false);
1903 ev
->hide_velocity ();
1905 if (_selection
.empty()) {
1906 PublicEditor
& editor (trackview
.editor());
1907 editor
.get_selection().remove (this);
1912 MidiRegionView::add_to_selection (CanvasNoteEvent
* ev
)
1914 bool add_mrv_selection
= false;
1916 if (_selection
.empty()) {
1917 add_mrv_selection
= true;
1920 if (_selection
.insert (ev
).second
) {
1921 ev
->set_selected (true);
1922 play_midi_note ((ev
)->note());
1925 if (add_mrv_selection
) {
1926 PublicEditor
& editor (trackview
.editor());
1927 editor
.get_selection().add (this);
1932 MidiRegionView::move_selection(double dx
, double dy
)
1934 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1935 (*i
)->move_event(dx
, dy
);
1940 MidiRegionView::note_dropped(CanvasNoteEvent
*, double dt
, int8_t dnote
)
1942 assert (!_selection
.empty());
1944 uint8_t lowest_note_in_selection
= 127;
1945 uint8_t highest_note_in_selection
= 0;
1946 uint8_t highest_note_difference
= 0;
1948 // find highest and lowest notes first
1950 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1951 uint8_t pitch
= (*i
)->note()->note();
1952 lowest_note_in_selection
= std::min(lowest_note_in_selection
, pitch
);
1953 highest_note_in_selection
= std::max(highest_note_in_selection
, pitch
);
1957 cerr << "dnote: " << (int) dnote << endl;
1958 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
1959 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
1960 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
1961 << int(highest_note_in_selection) << endl;
1962 cerr << "selection size: " << _selection.size() << endl;
1963 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
1966 // Make sure the note pitch does not exceed the MIDI standard range
1967 if (highest_note_in_selection
+ dnote
> 127) {
1968 highest_note_difference
= highest_note_in_selection
- 127;
1971 start_diff_command(_("move notes"));
1973 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end() ; ++i
) {
1975 nframes64_t start_frames
= beats_to_frames((*i
)->note()->time());
1978 start_frames
+= snap_frame_to_frame(trackview
.editor().pixel_to_frame(dt
));
1980 start_frames
-= snap_frame_to_frame(trackview
.editor().pixel_to_frame(-dt
));
1983 Evoral::MusicalTime new_time
= frames_to_beats(start_frames
);
1989 diff_add_change (*i
, MidiModel::DiffCommand::StartTime
, new_time
);
1991 uint8_t original_pitch
= (*i
)->note()->note();
1992 uint8_t new_pitch
= original_pitch
+ dnote
- highest_note_difference
;
1994 // keep notes in standard midi range
1995 clamp_to_0_127(new_pitch
);
1997 // keep original pitch if note is dragged outside valid midi range
1998 if ((original_pitch
!= 0 && new_pitch
== 0)
1999 || (original_pitch
!= 127 && new_pitch
== 127)) {
2000 new_pitch
= original_pitch
;
2003 lowest_note_in_selection
= std::min(lowest_note_in_selection
, new_pitch
);
2004 highest_note_in_selection
= std::max(highest_note_in_selection
, new_pitch
);
2006 diff_add_change (*i
, MidiModel::DiffCommand::NoteNumber
, new_pitch
);
2011 // care about notes being moved beyond the upper/lower bounds on the canvas
2012 if (lowest_note_in_selection
< midi_stream_view()->lowest_note() ||
2013 highest_note_in_selection
> midi_stream_view()->highest_note()) {
2014 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange
);
2019 MidiRegionView::snap_pixel_to_frame(double x
)
2021 PublicEditor
& editor
= trackview
.editor();
2022 // x is region relative, convert it to global absolute frames
2023 nframes64_t frame
= editor
.pixel_to_frame(x
) + _region
->position();
2024 editor
.snap_to(frame
);
2025 return frame
- _region
->position(); // convert back to region relative
2029 MidiRegionView::snap_frame_to_frame(nframes64_t x
)
2031 PublicEditor
& editor
= trackview
.editor();
2032 // x is region relative, convert it to global absolute frames
2033 nframes64_t frame
= x
+ _region
->position();
2034 editor
.snap_to(frame
);
2035 return frame
- _region
->position(); // convert back to region relative
2039 MidiRegionView::snap_to_pixel(double x
)
2041 return (double) trackview
.editor().frame_to_pixel(snap_pixel_to_frame(x
));
2045 MidiRegionView::get_position_pixels()
2047 nframes64_t region_frame
= get_position();
2048 return trackview
.editor().frame_to_pixel(region_frame
);
2052 MidiRegionView::get_end_position_pixels()
2054 nframes64_t frame
= get_position() + get_duration ();
2055 return trackview
.editor().frame_to_pixel(frame
);
2059 MidiRegionView::beats_to_frames(double beats
) const
2061 return _time_converter
.to(beats
);
2065 MidiRegionView::frames_to_beats(nframes64_t frames
) const
2067 return _time_converter
.from(frames
);
2071 MidiRegionView::begin_resizing (bool /*at_front*/)
2073 _resize_data
.clear();
2075 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2076 CanvasNote
*note
= dynamic_cast<CanvasNote
*> (*i
);
2078 // only insert CanvasNotes into the map
2080 NoteResizeData
*resize_data
= new NoteResizeData();
2081 resize_data
->canvas_note
= note
;
2083 // create a new SimpleRect from the note which will be the resize preview
2084 SimpleRect
*resize_rect
= new SimpleRect(
2085 *group
, note
->x1(), note
->y1(), note
->x2(), note
->y2());
2087 // calculate the colors: get the color settings
2088 uint32_t fill_color
= UINT_RGBA_CHANGE_A(
2089 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get(),
2092 // make the resize preview notes more transparent and bright
2093 fill_color
= UINT_INTERPOLATE(fill_color
, 0xFFFFFF40, 0.5);
2095 // calculate color based on note velocity
2096 resize_rect
->property_fill_color_rgba() = UINT_INTERPOLATE(
2097 CanvasNoteEvent::meter_style_fill_color(note
->note()->velocity(), note
->selected()),
2101 resize_rect
->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2102 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get());
2104 resize_data
->resize_rect
= resize_rect
;
2105 _resize_data
.push_back(resize_data
);
2110 /** Update resizing notes while user drags.
2111 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2112 * @param at_front which end of the note (true == note on, false == note off)
2113 * @param delta_x change in mouse position since the start of the drag
2114 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2115 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2116 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2117 * as the \a primary note.
2120 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote
* primary
, bool at_front
, double delta_x
, bool relative
)
2122 bool cursor_set
= false;
2124 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2125 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2126 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2131 current_x
= canvas_note
->x1() + delta_x
;
2133 current_x
= primary
->x1() + delta_x
;
2137 current_x
= canvas_note
->x2() + delta_x
;
2139 current_x
= primary
->x2() + delta_x
;
2144 resize_rect
->property_x1() = snap_to_pixel(current_x
);
2145 resize_rect
->property_x2() = canvas_note
->x2();
2147 resize_rect
->property_x2() = snap_to_pixel(current_x
);
2148 resize_rect
->property_x1() = canvas_note
->x1();
2154 beats
= snap_pixel_to_frame (current_x
);
2155 beats
= frames_to_beats (beats
);
2160 if (beats
< canvas_note
->note()->end_time()) {
2161 len
= canvas_note
->note()->time() - beats
;
2162 len
+= canvas_note
->note()->length();
2167 if (beats
>= canvas_note
->note()->end_time()) {
2168 len
= beats
- canvas_note
->note()->time();
2175 snprintf (buf
, sizeof (buf
), "%.3g beats", len
);
2176 trackview
.editor().show_verbose_canvas_cursor_with (buf
);
2185 /** Finish resizing notes when the user releases the mouse button.
2186 * Parameters the same as for \a update_resizing().
2189 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote
* primary
, bool at_front
, double delta_x
, bool relative
)
2191 start_diff_command(_("resize notes"));
2193 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2194 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2195 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2200 current_x
= canvas_note
->x1() + delta_x
;
2202 current_x
= primary
->x1() + delta_x
;
2206 current_x
= canvas_note
->x2() + delta_x
;
2208 current_x
= primary
->x2() + delta_x
;
2212 current_x
= snap_pixel_to_frame (current_x
);
2213 current_x
= frames_to_beats (current_x
);
2215 if (at_front
&& current_x
< canvas_note
->note()->end_time()) {
2216 diff_add_change (canvas_note
, MidiModel::DiffCommand::StartTime
, current_x
);
2218 double len
= canvas_note
->note()->time() - current_x
;
2219 len
+= canvas_note
->note()->length();
2222 /* XXX convert to beats */
2223 diff_add_change (canvas_note
, MidiModel::DiffCommand::Length
, len
);
2228 double len
= current_x
- canvas_note
->note()->time();
2231 /* XXX convert to beats */
2232 diff_add_change (canvas_note
, MidiModel::DiffCommand::Length
, len
);
2240 _resize_data
.clear();
2245 MidiRegionView::change_note_velocity(CanvasNoteEvent
* event
, int8_t velocity
, bool relative
)
2247 uint8_t new_velocity
;
2250 new_velocity
= event
->note()->velocity() + velocity
;
2251 clamp_to_0_127(new_velocity
);
2253 new_velocity
= velocity
;
2256 event
->set_selected (event
->selected()); // change color
2258 diff_add_change (event
, MidiModel::DiffCommand::Velocity
, new_velocity
);
2262 MidiRegionView::change_note_note (CanvasNoteEvent
* event
, int8_t note
, bool relative
)
2267 new_note
= event
->note()->note() + note
;
2272 clamp_to_0_127 (new_note
);
2273 diff_add_change (event
, MidiModel::DiffCommand::NoteNumber
, new_note
);
2277 MidiRegionView::trim_note (CanvasNoteEvent
* event
, Evoral::MusicalTime front_delta
, Evoral::MusicalTime end_delta
)
2279 bool change_start
= false;
2280 bool change_length
= false;
2281 Evoral::MusicalTime new_start
= 0;
2282 Evoral::MusicalTime new_length
= 0;
2284 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2286 front_delta: if positive - move the start of the note later in time (shortening it)
2287 if negative - move the start of the note earlier in time (lengthening it)
2289 end_delta: if positive - move the end of the note later in time (lengthening it)
2290 if negative - move the end of the note earlier in time (shortening it)
2294 if (front_delta
< 0) {
2296 if (event
->note()->time() < -front_delta
) {
2299 new_start
= event
->note()->time() + front_delta
; // moves earlier
2302 /* start moved toward zero, so move the end point out to where it used to be.
2303 Note that front_delta is negative, so this increases the length.
2306 new_length
= event
->note()->length() - front_delta
;
2307 change_start
= true;
2308 change_length
= true;
2312 Evoral::MusicalTime new_pos
= event
->note()->time() + front_delta
;
2314 if (new_pos
< event
->note()->end_time()) {
2315 new_start
= event
->note()->time() + front_delta
;
2316 /* start moved toward the end, so move the end point back to where it used to be */
2317 new_length
= event
->note()->length() - front_delta
;
2318 change_start
= true;
2319 change_length
= true;
2326 bool can_change
= true;
2327 if (end_delta
< 0) {
2328 if (event
->note()->length() < -end_delta
) {
2334 new_length
= event
->note()->length() + end_delta
;
2335 change_length
= true;
2340 diff_add_change (event
, MidiModel::DiffCommand::StartTime
, new_start
);
2343 if (change_length
) {
2344 diff_add_change (event
, MidiModel::DiffCommand::Length
, new_length
);
2349 MidiRegionView::change_note_time (CanvasNoteEvent
* event
, Evoral::MusicalTime delta
, bool relative
)
2351 Evoral::MusicalTime new_time
;
2355 if (event
->note()->time() < -delta
) {
2358 new_time
= event
->note()->time() + delta
;
2361 new_time
= event
->note()->time() + delta
;
2367 diff_add_change (event
, MidiModel::DiffCommand::StartTime
, new_time
);
2371 MidiRegionView::change_velocities (bool up
, bool fine
, bool allow_smush
)
2375 if (_selection
.empty()) {
2390 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2391 if ((*i
)->note()->velocity() + delta
== 0 || (*i
)->note()->velocity() + delta
== 127) {
2397 start_diff_command(_("change velocities"));
2399 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end();) {
2400 Selection::iterator next
= i
;
2402 change_note_velocity (*i
, delta
, true);
2408 if (!_selection
.empty()) {
2410 snprintf (buf
, sizeof (buf
), "Vel %d",
2411 (int) (*_selection
.begin())->note()->velocity());
2412 trackview
.editor().show_verbose_canvas_cursor_with (buf
);
2418 MidiRegionView::transpose (bool up
, bool fine
, bool allow_smush
)
2420 if (_selection
.empty()) {
2437 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2439 if ((int8_t) (*i
)->note()->note() + delta
<= 0) {
2443 if ((int8_t) (*i
)->note()->note() + delta
> 127) {
2450 start_diff_command (_("transpose"));
2452 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2453 Selection::iterator next
= i
;
2455 change_note_note (*i
, delta
, true);
2463 MidiRegionView::change_note_lengths (bool fine
, bool shorter
, bool start
, bool end
)
2465 Evoral::MusicalTime delta
;
2470 /* grab the current grid distance */
2472 delta
= trackview
.editor().get_grid_type_as_beats (success
, _region
->position());
2474 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2475 cerr
<< "Grid type not available as beats - TO BE FIXED\n";
2484 start_diff_command (_("change note lengths"));
2486 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2487 Selection::iterator next
= i
;
2490 /* note the negation of the delta for start */
2492 trim_note (*i
, (start
? -delta
: 0), (end
? delta
: 0));
2501 MidiRegionView::nudge_notes (bool forward
)
2503 if (_selection
.empty()) {
2507 /* pick a note as the point along the timeline to get the nudge distance.
2508 its not necessarily the earliest note, so we may want to pull the notes out
2509 into a vector and sort before using the first one.
2512 nframes64_t ref_point
= _region
->position() + beats_to_frames ((*(_selection
.begin()))->note()->time());
2514 nframes64_t distance
;
2516 if (trackview
.editor().snap_mode() == Editing::SnapOff
) {
2518 /* grid is off - use nudge distance */
2520 distance
= trackview
.editor().get_nudge_distance (ref_point
, unused
);
2526 nframes64_t next_pos
= ref_point
;
2529 /* XXX need check on max_frames, but that needs max_frames64 or something */
2532 if (next_pos
== 0) {
2538 trackview
.editor().snap_to (next_pos
, (forward
? 1 : -1), false);
2539 distance
= ref_point
- next_pos
;
2542 if (distance
== 0) {
2546 Evoral::MusicalTime delta
= frames_to_beats (fabs (distance
));
2552 start_diff_command (_("nudge"));
2554 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2555 Selection::iterator next
= i
;
2557 change_note_time (*i
, delta
, true);
2565 MidiRegionView::change_channel(uint8_t channel
)
2567 start_diff_command(_("change channel"));
2568 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2569 diff_add_change (*i
, MidiModel::DiffCommand::Channel
, channel
);
2577 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent
* ev
)
2579 if (_mouse_state
== SelectTouchDragging
) {
2580 note_selected (ev
, true);
2583 show_verbose_canvas_cursor (ev
->note ());
2587 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent
* note
)
2589 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2590 (*i
)->hide_velocity ();
2593 trackview
.editor().hide_verbose_canvas_cursor ();
2597 MidiRegionView::switch_source(boost::shared_ptr
<Source
> src
)
2599 boost::shared_ptr
<MidiSource
> msrc
= boost::dynamic_pointer_cast
<MidiSource
>(src
);
2601 display_model(msrc
->model());
2605 MidiRegionView::set_frame_color()
2608 if (_selected
&& should_show_selection
) {
2609 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase
.get();
2611 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase
.get();
2617 MidiRegionView::midi_channel_mode_changed(ChannelMode mode
, uint16_t mask
)
2621 case FilterChannels
:
2622 _force_channel
= -1;
2625 _force_channel
= mask
;
2626 mask
= 0xFFFF; // Show all notes as active (below)
2629 // Update notes for selection
2630 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2631 (*i
)->on_channel_selection_change(mask
);
2634 _last_channel_selection
= mask
;
2638 MidiRegionView::midi_patch_settings_changed(std::string model
, std::string custom_device_mode
)
2640 _model_name
= model
;
2641 _custom_device_mode
= custom_device_mode
;
2646 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op
)
2648 if (_selection
.empty()) {
2652 PublicEditor
& editor (trackview
.editor());
2657 editor
.get_cut_buffer().add (selection_as_cut_buffer());
2665 start_diff_command();
2667 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2673 diff_remove_note (*i
);
2683 MidiRegionView::selection_as_cut_buffer () const
2687 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2688 NoteType
* n
= (*i
)->note().get();
2689 notes
.insert (boost::shared_ptr
<NoteType
> (new NoteType (*n
)));
2692 MidiCutBuffer
* cb
= new MidiCutBuffer (trackview
.session());
2699 MidiRegionView::paste (nframes64_t pos
, float times
, const MidiCutBuffer
& mcb
)
2705 start_diff_command (_("paste"));
2707 Evoral::MusicalTime beat_delta
;
2708 Evoral::MusicalTime paste_pos_beats
;
2709 Evoral::MusicalTime duration
;
2710 Evoral::MusicalTime end_point
= 0;
2712 duration
= (*mcb
.notes().rbegin())->end_time() - (*mcb
.notes().begin())->time();
2713 paste_pos_beats
= frames_to_beats (pos
- _region
->position());
2714 beat_delta
= (*mcb
.notes().begin())->time() - paste_pos_beats
;
2715 paste_pos_beats
= 0;
2717 _selection
.clear ();
2719 for (int n
= 0; n
< (int) times
; ++n
) {
2721 for (Notes::const_iterator i
= mcb
.notes().begin(); i
!= mcb
.notes().end(); ++i
) {
2723 boost::shared_ptr
<NoteType
> copied_note (new NoteType (*((*i
).get())));
2724 copied_note
->set_time (paste_pos_beats
+ copied_note
->time() - beat_delta
);
2726 /* make all newly added notes selected */
2728 diff_add_note (copied_note
, true);
2729 end_point
= copied_note
->end_time();
2732 paste_pos_beats
+= duration
;
2735 /* if we pasted past the current end of the region, extend the region */
2737 nframes64_t end_frame
= _region
->position() + beats_to_frames (end_point
);
2738 nframes64_t region_end
= _region
->position() + _region
->length() - 1;
2740 if (end_frame
> region_end
) {
2742 trackview
.session()->begin_reversible_command (_("paste"));
2744 _region
->clear_history ();
2745 _region
->set_length (end_frame
, this);
2746 trackview
.session()->add_command (new StatefulDiffCommand (_region
));
2752 struct EventNoteTimeEarlyFirstComparator
{
2753 bool operator() (CanvasNoteEvent
* a
, CanvasNoteEvent
* b
) {
2754 return a
->note()->time() < b
->note()->time();
2759 MidiRegionView::time_sort_events ()
2761 if (!_sort_needed
) {
2765 EventNoteTimeEarlyFirstComparator cmp
;
2768 _sort_needed
= false;
2772 MidiRegionView::goto_next_note ()
2774 // nframes64_t pos = -1;
2775 bool use_next
= false;
2777 if (_events
.back()->selected()) {
2781 time_sort_events ();
2783 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2784 if ((*i
)->selected()) {
2787 } else if (use_next
) {
2789 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2794 /* use the first one */
2796 unique_select (_events
.front());
2801 MidiRegionView::goto_previous_note ()
2803 // nframes64_t pos = -1;
2804 bool use_next
= false;
2806 if (_events
.front()->selected()) {
2810 time_sort_events ();
2812 for (Events::reverse_iterator i
= _events
.rbegin(); i
!= _events
.rend(); ++i
) {
2813 if ((*i
)->selected()) {
2816 } else if (use_next
) {
2818 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2823 /* use the last one */
2825 unique_select (*(_events
.rbegin()));
2829 MidiRegionView::selection_as_notelist (Notes
& selected
, bool allow_all_if_none_selected
)
2831 bool had_selected
= false;
2833 time_sort_events ();
2835 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2836 if ((*i
)->selected()) {
2837 selected
.insert ((*i
)->note());
2838 had_selected
= true;
2842 if (allow_all_if_none_selected
&& !had_selected
) {
2843 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2844 selected
.insert ((*i
)->note());
2850 MidiRegionView::update_ghost_note (double x
, double y
)
2856 nframes64_t f
= trackview
.editor().pixel_to_frame (x
) + _region
->position ();
2857 trackview
.editor().snap_to (f
);
2858 f
-= _region
->position ();
2861 Evoral::MusicalTime beats
= trackview
.editor().get_grid_type_as_beats (success
, f
);
2866 double length
= frames_to_beats (snap_frame_to_frame (f
+ beats_to_frames (beats
)) - f
);
2868 _ghost_note
->note()->set_time (frames_to_beats (f
+ _region
->start()));
2869 _ghost_note
->note()->set_length (length
);
2870 _ghost_note
->note()->set_note (midi_stream_view()->y_to_note (y
));
2872 update_note (_ghost_note
);
2874 show_verbose_canvas_cursor (_ghost_note
->note ());
2878 MidiRegionView::create_ghost_note (double x
, double y
)
2883 boost::shared_ptr
<NoteType
> g (new NoteType
);
2884 _ghost_note
= new NoEventCanvasNote (*this, *group
, g
);
2885 update_ghost_note (x
, y
);
2886 _ghost_note
->show ();
2891 show_verbose_canvas_cursor (_ghost_note
->note ());
2895 MidiRegionView::snap_changed ()
2901 create_ghost_note (_last_ghost_x
, _last_ghost_y
);
2905 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr
<NoteType
> n
) const
2908 snprintf (buf
, sizeof (buf
), "%s (%d)\nVel %d",
2909 Evoral::midi_note_name (n
->note()).c_str(),
2911 (int) n
->velocity());
2912 trackview
.editor().show_verbose_canvas_cursor_with (buf
);
2916 MidiRegionView::drop_down_keys ()
2918 _mouse_state
= None
;
2922 MidiRegionView::maybe_select_by_position (GdkEventButton
* ev
, double x
, double y
)
2924 double note
= midi_stream_view()->y_to_note(y
);
2926 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
2928 cerr
<< "Selecting by position\n";
2930 uint16_t chn_mask
= mtv
->channel_selector().get_selected_channels();
2932 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::TertiaryModifier
)) {
2933 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchGreaterThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
2934 } else if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
2935 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchLessThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
2940 bool add_mrv_selection
= false;
2942 if (_selection
.empty()) {
2943 add_mrv_selection
= true;
2946 for (Events::iterator i
= e
.begin(); i
!= e
.end(); ++i
) {
2947 if (_selection
.insert (*i
).second
) {
2948 (*i
)->set_selected (true);
2952 if (add_mrv_selection
) {
2953 PublicEditor
& editor (trackview
.editor());
2954 editor
.get_selection().add (this);
2959 MidiRegionView::color_handler ()
2961 RegionView::color_handler ();
2963 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2964 (*i
)->set_selected ((*i
)->selected()); // will change color
2967 /* XXX probably more to do here */
2971 MidiRegionView::enable_display (bool yn
)
2973 RegionView::enable_display (yn
);