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
);
973 display_program_changes();
975 _marked_for_selection
.clear ();
976 _marked_for_velocity
.clear ();
978 /* we may have caused _events to contain things out of order (e.g. if a note
979 moved earlier or later). we don't generally need them in time order, but
980 make a note that a sort is required for those cases that require it.
987 MidiRegionView::display_program_changes()
989 boost::shared_ptr
<Evoral::Control
> control
= _model
->control(MidiPgmChangeAutomation
);
994 Glib::Mutex::Lock
lock (control
->list()->lock());
996 uint8_t channel
= control
->parameter().channel();
998 for (AutomationList::const_iterator event
= control
->list()->begin();
999 event
!= control
->list()->end(); ++event
) {
1000 double event_time
= (*event
)->when
;
1001 double program_number
= floor((*event
)->value
+ 0.5);
1003 // Get current value of bank select MSB at time of the program change
1004 Evoral::Parameter
bank_select_msb(MidiCCAutomation
, channel
, MIDI_CTL_MSB_BANK
);
1005 boost::shared_ptr
<Evoral::Control
> msb_control
= _model
->control(bank_select_msb
);
1007 if (msb_control
!= 0) {
1008 msb
= uint8_t(floor(msb_control
->get_float(true, event_time
) + 0.5));
1011 // Get current value of bank select LSB at time of the program change
1012 Evoral::Parameter
bank_select_lsb(MidiCCAutomation
, channel
, MIDI_CTL_LSB_BANK
);
1013 boost::shared_ptr
<Evoral::Control
> lsb_control
= _model
->control(bank_select_lsb
);
1015 if (lsb_control
!= 0) {
1016 lsb
= uint8_t(floor(lsb_control
->get_float(true, event_time
) + 0.5));
1019 MIDI::Name::PatchPrimaryKey
patch_key(msb
, lsb
, program_number
);
1021 boost::shared_ptr
<MIDI::Name::Patch
> patch
=
1022 MIDI::Name::MidiPatchManager::instance().find_patch(
1023 _model_name
, _custom_device_mode
, channel
, patch_key
);
1025 PCEvent
program_change(event_time
, uint8_t(program_number
), channel
);
1028 add_pgm_change(program_change
, patch
->name());
1031 snprintf(buf
, 4, "%d", int(program_number
));
1032 add_pgm_change(program_change
, buf
);
1038 MidiRegionView::display_sysexes()
1040 for (MidiModel::SysExes::const_iterator i
= _model
->sysexes().begin(); i
!= _model
->sysexes().end(); ++i
) {
1041 Evoral::MusicalTime time
= (*i
)->time();
1046 for (uint32_t b
= 0; b
< (*i
)->size(); ++b
) {
1047 str
<< int((*i
)->buffer()[b
]);
1048 if (b
!= (*i
)->size() -1) {
1052 string text
= str
.str();
1054 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
1056 const double x
= trackview
.editor().frame_to_pixel(beats_to_frames(time
));
1058 double height
= midi_stream_view()->contents_height();
1060 boost::shared_ptr
<CanvasSysEx
> sysex
= boost::shared_ptr
<CanvasSysEx
>(
1061 new CanvasSysEx(*this, *group
, text
, height
, x
, 1.0));
1063 // Show unless program change is beyond the region bounds
1064 if (time
- _region
->start() >= _region
->length() || time
< _region
->start()) {
1070 _sys_exes
.push_back(sysex
);
1075 MidiRegionView::~MidiRegionView ()
1077 in_destructor
= true;
1079 trackview
.editor().hide_verbose_canvas_cursor ();
1081 note_delete_connection
.disconnect ();
1083 delete _list_editor
;
1085 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1087 if (_active_notes
) {
1094 delete _diff_command
;
1098 MidiRegionView::region_resized (const PropertyChange
& what_changed
)
1100 RegionView::region_resized(what_changed
);
1102 if (what_changed
.contains (ARDOUR::Properties::position
)) {
1103 set_duration(_region
->length(), 0);
1104 if (_enable_display
) {
1111 MidiRegionView::reset_width_dependent_items (double pixel_width
)
1113 RegionView::reset_width_dependent_items(pixel_width
);
1114 assert(_pixel_width
== pixel_width
);
1116 if (_enable_display
) {
1122 MidiRegionView::set_height (double height
)
1124 static const double FUDGE
= 2.0;
1125 const double old_height
= _height
;
1126 RegionView::set_height(height
);
1127 _height
= height
- FUDGE
;
1129 apply_note_range(midi_stream_view()->lowest_note(),
1130 midi_stream_view()->highest_note(),
1131 height
!= old_height
+ FUDGE
);
1134 name_pixbuf
->raise_to_top();
1139 /** Apply the current note range from the stream view
1140 * by repositioning/hiding notes as necessary
1143 MidiRegionView::apply_note_range (uint8_t min
, uint8_t max
, bool force
)
1145 if (!_enable_display
) {
1149 if (!force
&& _current_range_min
== min
&& _current_range_max
== max
) {
1153 _current_range_min
= min
;
1154 _current_range_max
= max
;
1156 for (Events::const_iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1157 CanvasNoteEvent
* event
= *i
;
1158 boost::shared_ptr
<NoteType
> note (event
->note());
1160 if (note
->note() < _current_range_min
||
1161 note
->note() > _current_range_max
) {
1167 if (CanvasNote
* cnote
= dynamic_cast<CanvasNote
*>(event
)) {
1169 const double y1
= midi_stream_view()->note_to_y(note
->note());
1170 const double y2
= y1
+ floor(midi_stream_view()->note_height());
1172 cnote
->property_y1() = y1
;
1173 cnote
->property_y2() = y2
;
1175 } else if (CanvasHit
* chit
= dynamic_cast<CanvasHit
*>(event
)) {
1177 double x
= trackview
.editor().frame_to_pixel(
1178 beats_to_frames(note
->time()) - _region
->start());
1179 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1180 double y
= midi_stream_view()->note_to_y(event
->note()->note())
1181 + ((diamond_size
-2.0) / 4.0);
1183 chit
->set_height (diamond_size
);
1184 chit
->move (x
- chit
->x1(), y
- chit
->y1());
1191 MidiRegionView::add_ghost (TimeAxisView
& tv
)
1195 double unit_position
= _region
->position () / samples_per_unit
;
1196 MidiTimeAxisView
* mtv
= dynamic_cast<MidiTimeAxisView
*>(&tv
);
1197 MidiGhostRegion
* ghost
;
1199 if (mtv
&& mtv
->midi_view()) {
1200 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1201 to allow having midi notes on top of note lines and waveforms.
1203 ghost
= new MidiGhostRegion (*mtv
->midi_view(), trackview
, unit_position
);
1205 ghost
= new MidiGhostRegion (tv
, trackview
, unit_position
);
1208 ghost
->set_height ();
1209 ghost
->set_duration (_region
->length() / samples_per_unit
);
1210 ghosts
.push_back (ghost
);
1212 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1213 if ((note
= dynamic_cast<CanvasNote
*>(*i
)) != 0) {
1214 ghost
->add_note(note
);
1218 GhostRegion::CatchDeletion
.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost
, this, _1
), gui_context());
1224 /** Begin tracking note state for successive calls to add_event
1227 MidiRegionView::begin_write()
1229 assert(!_active_notes
);
1230 _active_notes
= new CanvasNote
*[128];
1231 for (unsigned i
=0; i
< 128; ++i
) {
1232 _active_notes
[i
] = 0;
1237 /** Destroy note state for add_event
1240 MidiRegionView::end_write()
1242 delete[] _active_notes
;
1244 _marked_for_selection
.clear();
1245 _marked_for_velocity
.clear();
1249 /** Resolve an active MIDI note (while recording).
1252 MidiRegionView::resolve_note(uint8_t note
, double end_time
)
1254 if (midi_view()->note_mode() != Sustained
) {
1258 if (_active_notes
&& _active_notes
[note
]) {
1259 const nframes64_t end_time_frames
= beats_to_frames(end_time
);
1260 _active_notes
[note
]->property_x2() = trackview
.editor().frame_to_pixel(end_time_frames
);
1261 _active_notes
[note
]->property_outline_what() = (guint32
) 0xF; // all edges
1262 _active_notes
[note
] = 0;
1267 /** Extend active notes to rightmost edge of region (if length is changed)
1270 MidiRegionView::extend_active_notes()
1272 if (!_active_notes
) {
1276 for (unsigned i
=0; i
< 128; ++i
) {
1277 if (_active_notes
[i
]) {
1278 _active_notes
[i
]->property_x2() = trackview
.editor().frame_to_pixel(_region
->length());
1284 MidiRegionView::play_midi_note(boost::shared_ptr
<NoteType
> note
)
1286 if (no_sound_notes
|| !trackview
.editor().sound_notes()) {
1290 RouteUI
* route_ui
= dynamic_cast<RouteUI
*> (&trackview
);
1293 route_ui
->midi_track()->write_immediate_event(
1294 note
->on_event().size(), note
->on_event().buffer());
1296 const double note_length_beats
= (note
->off_event().time() - note
->on_event().time());
1297 nframes_t note_length_ms
= beats_to_frames(note_length_beats
)
1298 * (1000 / (double)route_ui
->session()->nominal_frame_rate());
1299 Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(this, &MidiRegionView::play_midi_note_off
), note
),
1300 note_length_ms
, G_PRIORITY_DEFAULT
);
1304 MidiRegionView::play_midi_note_off(boost::shared_ptr
<NoteType
> note
)
1306 RouteUI
* route_ui
= dynamic_cast<RouteUI
*> (&trackview
);
1309 route_ui
->midi_track()->write_immediate_event(
1310 note
->off_event().size(), note
->off_event().buffer());
1316 MidiRegionView::note_in_region_range(const boost::shared_ptr
<NoteType
> note
, bool& visible
) const
1318 const nframes64_t note_start_frames
= beats_to_frames(note
->time());
1320 bool outside
= (note_start_frames
- _region
->start() >= _region
->length()) ||
1321 (note_start_frames
< _region
->start());
1323 visible
= (note
->note() >= midi_stream_view()->lowest_note()) &&
1324 (note
->note() <= midi_stream_view()->highest_note());
1330 MidiRegionView::update_note (CanvasNote
* ev
)
1332 boost::shared_ptr
<NoteType
> note
= ev
->note();
1334 const nframes64_t note_start_frames
= beats_to_frames(note
->time());
1335 const nframes64_t note_end_frames
= beats_to_frames(note
->end_time());
1337 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1338 const double y1
= midi_stream_view()->note_to_y(note
->note());
1339 const double note_endpixel
= trackview
.editor().frame_to_pixel(note_end_frames
- _region
->start());
1341 ev
->property_x1() = x
;
1342 ev
->property_y1() = y1
;
1343 if (note
->length() > 0) {
1344 ev
->property_x2() = note_endpixel
;
1346 ev
->property_x2() = trackview
.editor().frame_to_pixel(_region
->length());
1348 ev
->property_y2() = y1
+ floor(midi_stream_view()->note_height());
1350 if (note
->length() == 0) {
1351 if (_active_notes
) {
1352 assert(note
->note() < 128);
1353 // If this note is already active there's a stuck note,
1354 // finish the old note rectangle
1355 if (_active_notes
[note
->note()]) {
1356 CanvasNote
* const old_rect
= _active_notes
[note
->note()];
1357 boost::shared_ptr
<NoteType
> old_note
= old_rect
->note();
1358 old_rect
->property_x2() = x
;
1359 old_rect
->property_outline_what() = (guint32
) 0xF;
1361 _active_notes
[note
->note()] = ev
;
1363 /* outline all but right edge */
1364 ev
->property_outline_what() = (guint32
) (0x1 & 0x4 & 0x8);
1366 /* outline all edges */
1367 ev
->property_outline_what() = (guint32
) 0xF;
1372 MidiRegionView::update_hit (CanvasHit
* ev
)
1374 boost::shared_ptr
<NoteType
> note
= ev
->note();
1376 const nframes64_t note_start_frames
= beats_to_frames(note
->time());
1377 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1378 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1379 const double y
= midi_stream_view()->note_to_y(note
->note()) + ((diamond_size
-2) / 4.0);
1384 /** Add a MIDI note to the view (with length).
1386 * If in sustained mode, notes with length 0 will be considered active
1387 * notes, and resolve_note should be called when the corresponding note off
1388 * event arrives, to properly display the note.
1391 MidiRegionView::add_note(const boost::shared_ptr
<NoteType
> note
, bool visible
)
1393 CanvasNoteEvent
* event
= 0;
1395 assert(note
->time() >= 0);
1396 assert(midi_view()->note_mode() == Sustained
|| midi_view()->note_mode() == Percussive
);
1398 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
1400 if (midi_view()->note_mode() == Sustained
) {
1402 CanvasNote
* ev_rect
= new CanvasNote(*this, *group
, note
);
1404 update_note (ev_rect
);
1408 MidiGhostRegion
* gr
;
1410 for (std::vector
<GhostRegion
*>::iterator g
= ghosts
.begin(); g
!= ghosts
.end(); ++g
) {
1411 if ((gr
= dynamic_cast<MidiGhostRegion
*>(*g
)) != 0) {
1412 gr
->add_note(ev_rect
);
1416 } else if (midi_view()->note_mode() == Percussive
) {
1418 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1420 CanvasHit
* ev_diamond
= new CanvasHit(*this, *group
, diamond_size
, note
);
1422 update_hit (ev_diamond
);
1431 if (_marked_for_selection
.find(note
) != _marked_for_selection
.end()) {
1432 note_selected(event
, true);
1435 if (_marked_for_velocity
.find(note
) != _marked_for_velocity
.end()) {
1436 event
->show_velocity();
1438 event
->on_channel_selection_change(_last_channel_selection
);
1439 _events
.push_back(event
);
1450 MidiRegionView::add_note (uint8_t channel
, uint8_t number
, uint8_t velocity
,
1451 Evoral::MusicalTime pos
, Evoral::MusicalTime len
)
1453 boost::shared_ptr
<NoteType
> new_note (new NoteType (channel
, pos
, len
, number
, velocity
));
1455 start_diff_command (_("step add"));
1456 diff_add_note (new_note
, true, false);
1459 /* potentially extend region to hold new note */
1461 nframes64_t end_frame
= _region
->position() + beats_to_frames (new_note
->end_time());
1462 nframes64_t region_end
= _region
->position() + _region
->length() - 1;
1464 if (end_frame
> region_end
) {
1465 _region
->set_length (end_frame
, this);
1472 MidiRegionView::add_pgm_change(PCEvent
& program
, const string
& displaytext
)
1474 assert(program
.time
>= 0);
1476 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
1477 const double x
= trackview
.editor().frame_to_pixel(beats_to_frames(program
.time
));
1479 double height
= midi_stream_view()->contents_height();
1481 boost::shared_ptr
<CanvasProgramChange
> pgm_change
= boost::shared_ptr
<CanvasProgramChange
>(
1482 new CanvasProgramChange(*this, *group
,
1487 _custom_device_mode
,
1488 program
.time
, program
.channel
, program
.value
));
1490 // Show unless program change is beyond the region bounds
1491 if (program
.time
- _region
->start() >= _region
->length() || program
.time
< _region
->start()) {
1497 _pgm_changes
.push_back(pgm_change
);
1501 MidiRegionView::get_patch_key_at(double time
, uint8_t channel
, MIDI::Name::PatchPrimaryKey
& key
)
1503 cerr
<< "getting patch key at " << time
<< " for channel " << channel
<< endl
;
1504 Evoral::Parameter
bank_select_msb(MidiCCAutomation
, channel
, MIDI_CTL_MSB_BANK
);
1505 boost::shared_ptr
<Evoral::Control
> msb_control
= _model
->control(bank_select_msb
);
1507 if (msb_control
!= 0) {
1508 msb
= int(msb_control
->get_float(true, time
));
1509 cerr
<< "got msb " << msb
;
1512 Evoral::Parameter
bank_select_lsb(MidiCCAutomation
, channel
, MIDI_CTL_LSB_BANK
);
1513 boost::shared_ptr
<Evoral::Control
> lsb_control
= _model
->control(bank_select_lsb
);
1515 if (lsb_control
!= 0) {
1516 lsb
= lsb_control
->get_float(true, time
);
1517 cerr
<< " got lsb " << lsb
;
1520 Evoral::Parameter
program_change(MidiPgmChangeAutomation
, channel
, 0);
1521 boost::shared_ptr
<Evoral::Control
> program_control
= _model
->control(program_change
);
1522 float program_number
= -1.0;
1523 if (program_control
!= 0) {
1524 program_number
= program_control
->get_float(true, time
);
1525 cerr
<< " got program " << program_number
<< endl
;
1528 key
.msb
= (int) floor(msb
+ 0.5);
1529 key
.lsb
= (int) floor(lsb
+ 0.5);
1530 key
.program_number
= (int) floor(program_number
+ 0.5);
1531 assert(key
.is_sane());
1536 MidiRegionView::alter_program_change(PCEvent
& old_program
, const MIDI::Name::PatchPrimaryKey
& new_patch
)
1538 // TODO: Get the real event here and alter them at the original times
1539 Evoral::Parameter
bank_select_msb(MidiCCAutomation
, old_program
.channel
, MIDI_CTL_MSB_BANK
);
1540 boost::shared_ptr
<Evoral::Control
> msb_control
= _model
->control(bank_select_msb
);
1541 if (msb_control
!= 0) {
1542 msb_control
->set_float(float(new_patch
.msb
), true, old_program
.time
);
1545 // TODO: Get the real event here and alter them at the original times
1546 Evoral::Parameter
bank_select_lsb(MidiCCAutomation
, old_program
.channel
, MIDI_CTL_LSB_BANK
);
1547 boost::shared_ptr
<Evoral::Control
> lsb_control
= _model
->control(bank_select_lsb
);
1548 if (lsb_control
!= 0) {
1549 lsb_control
->set_float(float(new_patch
.lsb
), true, old_program
.time
);
1552 Evoral::Parameter
program_change(MidiPgmChangeAutomation
, old_program
.channel
, 0);
1553 boost::shared_ptr
<Evoral::Control
> program_control
= _model
->control(program_change
);
1555 assert(program_control
!= 0);
1556 program_control
->set_float(float(new_patch
.program_number
), true, old_program
.time
);
1562 MidiRegionView::program_selected(CanvasProgramChange
& program
, const MIDI::Name::PatchPrimaryKey
& new_patch
)
1564 PCEvent
program_change_event(program
.event_time(), program
.program(), program
.channel());
1565 alter_program_change(program_change_event
, new_patch
);
1569 MidiRegionView::previous_program(CanvasProgramChange
& program
)
1571 MIDI::Name::PatchPrimaryKey key
;
1572 get_patch_key_at(program
.event_time(), program
.channel(), key
);
1574 boost::shared_ptr
<MIDI::Name::Patch
> patch
=
1575 MIDI::Name::MidiPatchManager::instance().previous_patch(
1577 _custom_device_mode
,
1581 PCEvent
program_change_event(program
.event_time(), program
.program(), program
.channel());
1583 alter_program_change(program_change_event
, patch
->patch_primary_key());
1588 MidiRegionView::next_program(CanvasProgramChange
& program
)
1590 MIDI::Name::PatchPrimaryKey key
;
1591 get_patch_key_at(program
.event_time(), program
.channel(), key
);
1593 boost::shared_ptr
<MIDI::Name::Patch
> patch
=
1594 MIDI::Name::MidiPatchManager::instance().next_patch(
1596 _custom_device_mode
,
1600 PCEvent
program_change_event(program
.event_time(), program
.program(), program
.channel());
1602 alter_program_change(program_change_event
, patch
->patch_primary_key());
1607 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent
* cne
)
1609 if (_selection
.empty()) {
1613 if (_selection
.erase (cne
) > 0) {
1614 cerr
<< "Erased a CNE from selection\n";
1619 MidiRegionView::delete_selection()
1621 if (_selection
.empty()) {
1625 start_diff_command (_("delete selection"));
1627 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1628 if ((*i
)->selected()) {
1629 _diff_command
->remove((*i
)->note());
1639 MidiRegionView::delete_note (boost::shared_ptr
<NoteType
> n
)
1641 start_diff_command (_("delete note"));
1642 _diff_command
->remove (n
);
1645 trackview
.editor().hide_verbose_canvas_cursor ();
1649 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent
* ev
)
1651 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1652 if ((*i
)->selected() && (*i
) != ev
) {
1653 (*i
)->set_selected(false);
1654 (*i
)->hide_velocity();
1662 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent
* ev
)
1664 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
1667 Selection::iterator tmp
= i
;
1670 (*i
)->set_selected (false);
1671 _selection
.erase (i
);
1680 /* don't bother with removing this regionview from the editor selection,
1681 since we're about to add another note, and thus put/keep this
1682 regionview in the editor selection.
1685 if (!ev
->selected()) {
1686 add_to_selection (ev
);
1691 MidiRegionView::select_matching_notes (uint8_t notenum
, uint16_t channel_mask
, bool add
, bool extend
)
1693 uint8_t low_note
= 127;
1694 uint8_t high_note
= 0;
1695 MidiModel::Notes
& notes (_model
->notes());
1696 _optimization_iterator
= _events
.begin();
1702 if (extend
&& _selection
.empty()) {
1708 /* scan existing selection to get note range */
1710 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1711 if ((*i
)->note()->note() < low_note
) {
1712 low_note
= (*i
)->note()->note();
1714 if ((*i
)->note()->note() > high_note
) {
1715 high_note
= (*i
)->note()->note();
1719 low_note
= min (low_note
, notenum
);
1720 high_note
= max (high_note
, notenum
);
1723 no_sound_notes
= true;
1725 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1727 boost::shared_ptr
<NoteType
> note (*n
);
1728 CanvasNoteEvent
* cne
;
1729 bool select
= false;
1731 if (((1 << note
->channel()) & channel_mask
) != 0) {
1733 if ((note
->note() >= low_note
&& note
->note() <= high_note
)) {
1736 } else if (note
->note() == notenum
) {
1742 if ((cne
= find_canvas_note (note
)) != 0) {
1743 // extend is false because we've taken care of it,
1744 // since it extends by time range, not pitch.
1745 note_selected (cne
, add
, false);
1749 add
= true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1753 no_sound_notes
= false;
1757 MidiRegionView::toggle_matching_notes (uint8_t notenum
, uint16_t channel_mask
)
1759 MidiModel::Notes
& notes (_model
->notes());
1760 _optimization_iterator
= _events
.begin();
1762 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1764 boost::shared_ptr
<NoteType
> note (*n
);
1765 CanvasNoteEvent
* cne
;
1767 if (note
->note() == notenum
&& (((0x0001 << note
->channel()) & channel_mask
) != 0)) {
1768 if ((cne
= find_canvas_note (note
)) != 0) {
1769 if (cne
->selected()) {
1770 note_deselected (cne
);
1772 note_selected (cne
, true, false);
1780 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent
* ev
, bool add
, bool extend
)
1783 clear_selection_except(ev
);
1788 if (!ev
->selected()) {
1789 add_to_selection (ev
);
1793 /* find end of latest note selected, select all between that and the start of "ev" */
1795 Evoral::MusicalTime earliest
= DBL_MAX
;
1796 Evoral::MusicalTime latest
= 0;
1798 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1799 if ((*i
)->note()->end_time() > latest
) {
1800 latest
= (*i
)->note()->end_time();
1802 if ((*i
)->note()->time() < earliest
) {
1803 earliest
= (*i
)->note()->time();
1807 if (ev
->note()->end_time() > latest
) {
1808 latest
= ev
->note()->end_time();
1811 if (ev
->note()->time() < earliest
) {
1812 earliest
= ev
->note()->time();
1815 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1817 /* find notes entirely within OR spanning the earliest..latest range */
1819 if (((*i
)->note()->time() >= earliest
&& (*i
)->note()->end_time() <= latest
) ||
1820 ((*i
)->note()->time() <= earliest
&& (*i
)->note()->end_time() >= latest
)) {
1821 add_to_selection (*i
);
1825 /* if events were guaranteed to be time sorted, we could do this.
1826 but as of sept 10th 2009, they no longer are.
1829 if ((*i
)->note()->time() > latest
) {
1838 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent
* ev
)
1840 remove_from_selection (ev
);
1844 MidiRegionView::update_drag_selection(double x1
, double x2
, double y1
, double y2
)
1854 // TODO: Make this faster by storing the last updated selection rect, and only
1855 // adjusting things that are in the area that appears/disappeared.
1856 // We probably need a tree to be able to find events in O(log(n)) time.
1858 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1860 /* check if any corner of the note is inside the rect
1863 1) this is computing "touched by", not "contained by" the rect.
1864 2) this does not require that events be sorted in time.
1867 const double ix1
= (*i
)->x1();
1868 const double ix2
= (*i
)->x2();
1869 const double iy1
= (*i
)->y1();
1870 const double iy2
= (*i
)->y2();
1872 if ((ix1
>= x1
&& ix1
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
1873 (ix1
>= x1
&& ix1
<= x2
&& iy2
>= y1
&& iy2
<= y2
) ||
1874 (ix2
>= x1
&& ix2
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
1875 (ix2
>= x1
&& ix2
<= x2
&& iy2
>= y1
&& iy2
<= y2
)) {
1878 if (!(*i
)->selected()) {
1879 add_to_selection (*i
);
1881 } else if ((*i
)->selected()) {
1882 // Not inside rectangle
1883 remove_from_selection (*i
);
1889 MidiRegionView::remove_from_selection (CanvasNoteEvent
* ev
)
1891 Selection::iterator i
= _selection
.find (ev
);
1893 if (i
!= _selection
.end()) {
1894 _selection
.erase (i
);
1897 ev
->set_selected (false);
1898 ev
->hide_velocity ();
1900 if (_selection
.empty()) {
1901 PublicEditor
& editor (trackview
.editor());
1902 editor
.get_selection().remove (this);
1907 MidiRegionView::add_to_selection (CanvasNoteEvent
* ev
)
1909 bool add_mrv_selection
= false;
1911 if (_selection
.empty()) {
1912 add_mrv_selection
= true;
1915 if (_selection
.insert (ev
).second
) {
1916 ev
->set_selected (true);
1917 play_midi_note ((ev
)->note());
1920 if (add_mrv_selection
) {
1921 PublicEditor
& editor (trackview
.editor());
1922 editor
.get_selection().add (this);
1927 MidiRegionView::move_selection(double dx
, double dy
)
1929 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1930 (*i
)->move_event(dx
, dy
);
1935 MidiRegionView::note_dropped(CanvasNoteEvent
*, double dt
, int8_t dnote
)
1937 assert (!_selection
.empty());
1939 uint8_t lowest_note_in_selection
= 127;
1940 uint8_t highest_note_in_selection
= 0;
1941 uint8_t highest_note_difference
= 0;
1943 // find highest and lowest notes first
1945 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1946 uint8_t pitch
= (*i
)->note()->note();
1947 lowest_note_in_selection
= std::min(lowest_note_in_selection
, pitch
);
1948 highest_note_in_selection
= std::max(highest_note_in_selection
, pitch
);
1952 cerr << "dnote: " << (int) dnote << endl;
1953 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
1954 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
1955 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
1956 << int(highest_note_in_selection) << endl;
1957 cerr << "selection size: " << _selection.size() << endl;
1958 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
1961 // Make sure the note pitch does not exceed the MIDI standard range
1962 if (highest_note_in_selection
+ dnote
> 127) {
1963 highest_note_difference
= highest_note_in_selection
- 127;
1966 start_diff_command(_("move notes"));
1968 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end() ; ++i
) {
1970 nframes64_t start_frames
= beats_to_frames((*i
)->note()->time());
1973 start_frames
+= snap_frame_to_frame(trackview
.editor().pixel_to_frame(dt
));
1975 start_frames
-= snap_frame_to_frame(trackview
.editor().pixel_to_frame(-dt
));
1978 Evoral::MusicalTime new_time
= frames_to_beats(start_frames
);
1984 diff_add_change (*i
, MidiModel::DiffCommand::StartTime
, new_time
);
1986 uint8_t original_pitch
= (*i
)->note()->note();
1987 uint8_t new_pitch
= original_pitch
+ dnote
- highest_note_difference
;
1989 // keep notes in standard midi range
1990 clamp_to_0_127(new_pitch
);
1992 // keep original pitch if note is dragged outside valid midi range
1993 if ((original_pitch
!= 0 && new_pitch
== 0)
1994 || (original_pitch
!= 127 && new_pitch
== 127)) {
1995 new_pitch
= original_pitch
;
1998 lowest_note_in_selection
= std::min(lowest_note_in_selection
, new_pitch
);
1999 highest_note_in_selection
= std::max(highest_note_in_selection
, new_pitch
);
2001 diff_add_change (*i
, MidiModel::DiffCommand::NoteNumber
, new_pitch
);
2006 // care about notes being moved beyond the upper/lower bounds on the canvas
2007 if (lowest_note_in_selection
< midi_stream_view()->lowest_note() ||
2008 highest_note_in_selection
> midi_stream_view()->highest_note()) {
2009 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange
);
2014 MidiRegionView::snap_pixel_to_frame(double x
)
2016 PublicEditor
& editor
= trackview
.editor();
2017 // x is region relative, convert it to global absolute frames
2018 nframes64_t frame
= editor
.pixel_to_frame(x
) + _region
->position();
2019 editor
.snap_to(frame
);
2020 return frame
- _region
->position(); // convert back to region relative
2024 MidiRegionView::snap_frame_to_frame(nframes64_t x
)
2026 PublicEditor
& editor
= trackview
.editor();
2027 // x is region relative, convert it to global absolute frames
2028 nframes64_t frame
= x
+ _region
->position();
2029 editor
.snap_to(frame
);
2030 return frame
- _region
->position(); // convert back to region relative
2034 MidiRegionView::snap_to_pixel(double x
)
2036 return (double) trackview
.editor().frame_to_pixel(snap_pixel_to_frame(x
));
2040 MidiRegionView::get_position_pixels()
2042 nframes64_t region_frame
= get_position();
2043 return trackview
.editor().frame_to_pixel(region_frame
);
2047 MidiRegionView::get_end_position_pixels()
2049 nframes64_t frame
= get_position() + get_duration ();
2050 return trackview
.editor().frame_to_pixel(frame
);
2054 MidiRegionView::beats_to_frames(double beats
) const
2056 return _time_converter
.to(beats
);
2060 MidiRegionView::frames_to_beats(nframes64_t frames
) const
2062 return _time_converter
.from(frames
);
2066 MidiRegionView::begin_resizing (bool /*at_front*/)
2068 _resize_data
.clear();
2070 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2071 CanvasNote
*note
= dynamic_cast<CanvasNote
*> (*i
);
2073 // only insert CanvasNotes into the map
2075 NoteResizeData
*resize_data
= new NoteResizeData();
2076 resize_data
->canvas_note
= note
;
2078 // create a new SimpleRect from the note which will be the resize preview
2079 SimpleRect
*resize_rect
= new SimpleRect(
2080 *group
, note
->x1(), note
->y1(), note
->x2(), note
->y2());
2082 // calculate the colors: get the color settings
2083 uint32_t fill_color
= UINT_RGBA_CHANGE_A(
2084 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get(),
2087 // make the resize preview notes more transparent and bright
2088 fill_color
= UINT_INTERPOLATE(fill_color
, 0xFFFFFF40, 0.5);
2090 // calculate color based on note velocity
2091 resize_rect
->property_fill_color_rgba() = UINT_INTERPOLATE(
2092 CanvasNoteEvent::meter_style_fill_color(note
->note()->velocity(), note
->selected()),
2096 resize_rect
->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2097 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get());
2099 resize_data
->resize_rect
= resize_rect
;
2100 _resize_data
.push_back(resize_data
);
2105 /** Update resizing notes while user drags.
2106 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2107 * @param at_front which end of the note (true == note on, false == note off)
2108 * @param delta_x change in mouse position since the start of the drag
2109 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2110 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2111 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2112 * as the \a primary note.
2115 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote
* primary
, bool at_front
, double delta_x
, bool relative
)
2117 bool cursor_set
= false;
2119 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2120 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2121 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2126 current_x
= canvas_note
->x1() + delta_x
;
2128 current_x
= primary
->x1() + delta_x
;
2132 current_x
= canvas_note
->x2() + delta_x
;
2134 current_x
= primary
->x2() + delta_x
;
2139 resize_rect
->property_x1() = snap_to_pixel(current_x
);
2140 resize_rect
->property_x2() = canvas_note
->x2();
2142 resize_rect
->property_x2() = snap_to_pixel(current_x
);
2143 resize_rect
->property_x1() = canvas_note
->x1();
2149 beats
= snap_pixel_to_frame (current_x
);
2150 beats
= frames_to_beats (beats
);
2155 if (beats
< canvas_note
->note()->end_time()) {
2156 len
= canvas_note
->note()->time() - beats
;
2157 len
+= canvas_note
->note()->length();
2162 if (beats
>= canvas_note
->note()->end_time()) {
2163 len
= beats
- canvas_note
->note()->time();
2170 snprintf (buf
, sizeof (buf
), "%.3g beats", len
);
2171 trackview
.editor().show_verbose_canvas_cursor_with (buf
);
2180 /** Finish resizing notes when the user releases the mouse button.
2181 * Parameters the same as for \a update_resizing().
2184 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote
* primary
, bool at_front
, double delta_x
, bool relative
)
2186 start_diff_command(_("resize notes"));
2188 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2189 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2190 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2195 current_x
= canvas_note
->x1() + delta_x
;
2197 current_x
= primary
->x1() + delta_x
;
2201 current_x
= canvas_note
->x2() + delta_x
;
2203 current_x
= primary
->x2() + delta_x
;
2207 current_x
= snap_pixel_to_frame (current_x
);
2208 current_x
= frames_to_beats (current_x
);
2210 if (at_front
&& current_x
< canvas_note
->note()->end_time()) {
2211 diff_add_change (canvas_note
, MidiModel::DiffCommand::StartTime
, current_x
);
2213 double len
= canvas_note
->note()->time() - current_x
;
2214 len
+= canvas_note
->note()->length();
2217 /* XXX convert to beats */
2218 diff_add_change (canvas_note
, MidiModel::DiffCommand::Length
, len
);
2223 double len
= current_x
- canvas_note
->note()->time();
2226 /* XXX convert to beats */
2227 diff_add_change (canvas_note
, MidiModel::DiffCommand::Length
, len
);
2235 _resize_data
.clear();
2240 MidiRegionView::change_note_velocity(CanvasNoteEvent
* event
, int8_t velocity
, bool relative
)
2242 uint8_t new_velocity
;
2245 new_velocity
= event
->note()->velocity() + velocity
;
2246 clamp_to_0_127(new_velocity
);
2248 new_velocity
= velocity
;
2251 event
->set_selected (event
->selected()); // change color
2253 diff_add_change (event
, MidiModel::DiffCommand::Velocity
, new_velocity
);
2257 MidiRegionView::change_note_note (CanvasNoteEvent
* event
, int8_t note
, bool relative
)
2262 new_note
= event
->note()->note() + note
;
2267 clamp_to_0_127 (new_note
);
2268 diff_add_change (event
, MidiModel::DiffCommand::NoteNumber
, new_note
);
2272 MidiRegionView::trim_note (CanvasNoteEvent
* event
, Evoral::MusicalTime front_delta
, Evoral::MusicalTime end_delta
)
2274 bool change_start
= false;
2275 bool change_length
= false;
2276 Evoral::MusicalTime new_start
= 0;
2277 Evoral::MusicalTime new_length
= 0;
2279 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2281 front_delta: if positive - move the start of the note later in time (shortening it)
2282 if negative - move the start of the note earlier in time (lengthening it)
2284 end_delta: if positive - move the end of the note later in time (lengthening it)
2285 if negative - move the end of the note earlier in time (shortening it)
2289 if (front_delta
< 0) {
2291 if (event
->note()->time() < -front_delta
) {
2294 new_start
= event
->note()->time() + front_delta
; // moves earlier
2297 /* start moved toward zero, so move the end point out to where it used to be.
2298 Note that front_delta is negative, so this increases the length.
2301 new_length
= event
->note()->length() - front_delta
;
2302 change_start
= true;
2303 change_length
= true;
2307 Evoral::MusicalTime new_pos
= event
->note()->time() + front_delta
;
2309 if (new_pos
< event
->note()->end_time()) {
2310 new_start
= event
->note()->time() + front_delta
;
2311 /* start moved toward the end, so move the end point back to where it used to be */
2312 new_length
= event
->note()->length() - front_delta
;
2313 change_start
= true;
2314 change_length
= true;
2321 bool can_change
= true;
2322 if (end_delta
< 0) {
2323 if (event
->note()->length() < -end_delta
) {
2329 new_length
= event
->note()->length() + end_delta
;
2330 change_length
= true;
2335 diff_add_change (event
, MidiModel::DiffCommand::StartTime
, new_start
);
2338 if (change_length
) {
2339 diff_add_change (event
, MidiModel::DiffCommand::Length
, new_length
);
2344 MidiRegionView::change_note_time (CanvasNoteEvent
* event
, Evoral::MusicalTime delta
, bool relative
)
2346 Evoral::MusicalTime new_time
;
2350 if (event
->note()->time() < -delta
) {
2353 new_time
= event
->note()->time() + delta
;
2356 new_time
= event
->note()->time() + delta
;
2362 diff_add_change (event
, MidiModel::DiffCommand::StartTime
, new_time
);
2366 MidiRegionView::change_velocities (bool up
, bool fine
, bool allow_smush
)
2370 if (_selection
.empty()) {
2385 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2386 if ((*i
)->note()->velocity() + delta
== 0 || (*i
)->note()->velocity() + delta
== 127) {
2392 start_diff_command(_("change velocities"));
2394 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end();) {
2395 Selection::iterator next
= i
;
2397 change_note_velocity (*i
, delta
, true);
2401 if (!_selection
.empty()) {
2403 snprintf (buf
, sizeof (buf
), "Vel %d",
2404 (int) (*_selection
.begin())->note()->velocity());
2405 trackview
.editor().show_verbose_canvas_cursor_with (buf
);
2413 MidiRegionView::transpose (bool up
, bool fine
, bool allow_smush
)
2415 if (_selection
.empty()) {
2432 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2434 if ((int8_t) (*i
)->note()->note() + delta
<= 0) {
2438 if ((int8_t) (*i
)->note()->note() + delta
> 127) {
2445 start_diff_command (_("transpose"));
2447 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2448 Selection::iterator next
= i
;
2450 change_note_note (*i
, delta
, true);
2458 MidiRegionView::change_note_lengths (bool fine
, bool shorter
, bool start
, bool end
)
2460 Evoral::MusicalTime delta
;
2465 /* grab the current grid distance */
2467 delta
= trackview
.editor().get_grid_type_as_beats (success
, _region
->position());
2469 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2470 cerr
<< "Grid type not available as beats - TO BE FIXED\n";
2479 start_diff_command (_("change note lengths"));
2481 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2482 Selection::iterator next
= i
;
2485 /* note the negation of the delta for start */
2487 trim_note (*i
, (start
? -delta
: 0), (end
? delta
: 0));
2496 MidiRegionView::nudge_notes (bool forward
)
2498 if (_selection
.empty()) {
2502 /* pick a note as the point along the timeline to get the nudge distance.
2503 its not necessarily the earliest note, so we may want to pull the notes out
2504 into a vector and sort before using the first one.
2507 nframes64_t ref_point
= _region
->position() + beats_to_frames ((*(_selection
.begin()))->note()->time());
2509 nframes64_t distance
;
2511 if (trackview
.editor().snap_mode() == Editing::SnapOff
) {
2513 /* grid is off - use nudge distance */
2515 distance
= trackview
.editor().get_nudge_distance (ref_point
, unused
);
2521 nframes64_t next_pos
= ref_point
;
2524 /* XXX need check on max_frames, but that needs max_frames64 or something */
2527 if (next_pos
== 0) {
2533 trackview
.editor().snap_to (next_pos
, (forward
? 1 : -1), false);
2534 distance
= ref_point
- next_pos
;
2537 if (distance
== 0) {
2541 Evoral::MusicalTime delta
= frames_to_beats (fabs (distance
));
2547 start_diff_command (_("nudge"));
2549 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2550 Selection::iterator next
= i
;
2552 change_note_time (*i
, delta
, true);
2560 MidiRegionView::change_channel(uint8_t channel
)
2562 start_diff_command(_("change channel"));
2563 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2564 diff_add_change (*i
, MidiModel::DiffCommand::Channel
, channel
);
2572 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent
* ev
)
2574 if (_mouse_state
== SelectTouchDragging
) {
2575 note_selected (ev
, true);
2578 show_verbose_canvas_cursor (ev
->note ());
2582 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent
* note
)
2584 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2585 (*i
)->hide_velocity ();
2588 trackview
.editor().hide_verbose_canvas_cursor ();
2592 MidiRegionView::switch_source(boost::shared_ptr
<Source
> src
)
2594 boost::shared_ptr
<MidiSource
> msrc
= boost::dynamic_pointer_cast
<MidiSource
>(src
);
2596 display_model(msrc
->model());
2600 MidiRegionView::set_frame_color()
2603 if (_selected
&& should_show_selection
) {
2604 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase
.get();
2606 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase
.get();
2612 MidiRegionView::midi_channel_mode_changed(ChannelMode mode
, uint16_t mask
)
2616 case FilterChannels
:
2617 _force_channel
= -1;
2620 _force_channel
= mask
;
2621 mask
= 0xFFFF; // Show all notes as active (below)
2624 // Update notes for selection
2625 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2626 (*i
)->on_channel_selection_change(mask
);
2629 _last_channel_selection
= mask
;
2633 MidiRegionView::midi_patch_settings_changed(std::string model
, std::string custom_device_mode
)
2635 _model_name
= model
;
2636 _custom_device_mode
= custom_device_mode
;
2641 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op
)
2643 if (_selection
.empty()) {
2647 PublicEditor
& editor (trackview
.editor());
2652 editor
.get_cut_buffer().add (selection_as_cut_buffer());
2660 start_diff_command();
2662 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2668 diff_remove_note (*i
);
2678 MidiRegionView::selection_as_cut_buffer () const
2682 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2683 NoteType
* n
= (*i
)->note().get();
2684 notes
.insert (boost::shared_ptr
<NoteType
> (new NoteType (*n
)));
2687 MidiCutBuffer
* cb
= new MidiCutBuffer (trackview
.session());
2694 MidiRegionView::paste (nframes64_t pos
, float times
, const MidiCutBuffer
& mcb
)
2700 start_diff_command (_("paste"));
2702 Evoral::MusicalTime beat_delta
;
2703 Evoral::MusicalTime paste_pos_beats
;
2704 Evoral::MusicalTime duration
;
2705 Evoral::MusicalTime end_point
= 0;
2707 duration
= (*mcb
.notes().rbegin())->end_time() - (*mcb
.notes().begin())->time();
2708 paste_pos_beats
= frames_to_beats (pos
- _region
->position());
2709 beat_delta
= (*mcb
.notes().begin())->time() - paste_pos_beats
;
2710 paste_pos_beats
= 0;
2712 _selection
.clear ();
2714 for (int n
= 0; n
< (int) times
; ++n
) {
2716 for (Notes::const_iterator i
= mcb
.notes().begin(); i
!= mcb
.notes().end(); ++i
) {
2718 boost::shared_ptr
<NoteType
> copied_note (new NoteType (*((*i
).get())));
2719 copied_note
->set_time (paste_pos_beats
+ copied_note
->time() - beat_delta
);
2721 /* make all newly added notes selected */
2723 diff_add_note (copied_note
, true);
2724 end_point
= copied_note
->end_time();
2727 paste_pos_beats
+= duration
;
2730 /* if we pasted past the current end of the region, extend the region */
2732 nframes64_t end_frame
= _region
->position() + beats_to_frames (end_point
);
2733 nframes64_t region_end
= _region
->position() + _region
->length() - 1;
2735 if (end_frame
> region_end
) {
2737 trackview
.session()->begin_reversible_command (_("paste"));
2739 _region
->clear_history ();
2740 _region
->set_length (end_frame
, this);
2741 trackview
.session()->add_command (new StatefulDiffCommand (_region
));
2747 struct EventNoteTimeEarlyFirstComparator
{
2748 bool operator() (CanvasNoteEvent
* a
, CanvasNoteEvent
* b
) {
2749 return a
->note()->time() < b
->note()->time();
2754 MidiRegionView::time_sort_events ()
2756 if (!_sort_needed
) {
2760 EventNoteTimeEarlyFirstComparator cmp
;
2763 _sort_needed
= false;
2767 MidiRegionView::goto_next_note ()
2769 // nframes64_t pos = -1;
2770 bool use_next
= false;
2772 if (_events
.back()->selected()) {
2776 time_sort_events ();
2778 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2779 if ((*i
)->selected()) {
2782 } else if (use_next
) {
2784 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2789 /* use the first one */
2791 unique_select (_events
.front());
2796 MidiRegionView::goto_previous_note ()
2798 // nframes64_t pos = -1;
2799 bool use_next
= false;
2801 if (_events
.front()->selected()) {
2805 time_sort_events ();
2807 for (Events::reverse_iterator i
= _events
.rbegin(); i
!= _events
.rend(); ++i
) {
2808 if ((*i
)->selected()) {
2811 } else if (use_next
) {
2813 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2818 /* use the last one */
2820 unique_select (*(_events
.rbegin()));
2824 MidiRegionView::selection_as_notelist (Notes
& selected
, bool allow_all_if_none_selected
)
2826 bool had_selected
= false;
2828 time_sort_events ();
2830 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2831 if ((*i
)->selected()) {
2832 selected
.insert ((*i
)->note());
2833 had_selected
= true;
2837 if (allow_all_if_none_selected
&& !had_selected
) {
2838 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2839 selected
.insert ((*i
)->note());
2845 MidiRegionView::update_ghost_note (double x
, double y
)
2851 nframes64_t f
= trackview
.editor().pixel_to_frame (x
) + _region
->position ();
2852 trackview
.editor().snap_to (f
);
2853 f
-= _region
->position ();
2856 Evoral::MusicalTime beats
= trackview
.editor().get_grid_type_as_beats (success
, f
);
2861 double length
= frames_to_beats (snap_frame_to_frame (f
+ beats_to_frames (beats
)) - f
);
2863 _ghost_note
->note()->set_time (frames_to_beats (f
+ _region
->start()));
2864 _ghost_note
->note()->set_length (length
);
2865 _ghost_note
->note()->set_note (midi_stream_view()->y_to_note (y
));
2867 update_note (_ghost_note
);
2869 show_verbose_canvas_cursor (_ghost_note
->note ());
2873 MidiRegionView::create_ghost_note (double x
, double y
)
2878 boost::shared_ptr
<NoteType
> g (new NoteType
);
2879 _ghost_note
= new NoEventCanvasNote (*this, *group
, g
);
2880 update_ghost_note (x
, y
);
2881 _ghost_note
->show ();
2886 show_verbose_canvas_cursor (_ghost_note
->note ());
2890 MidiRegionView::snap_changed ()
2896 create_ghost_note (_last_ghost_x
, _last_ghost_y
);
2900 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr
<NoteType
> n
) const
2903 snprintf (buf
, sizeof (buf
), "%s (%d)\nVel %d",
2904 Evoral::midi_note_name (n
->note()).c_str(),
2906 (int) n
->velocity());
2907 trackview
.editor().show_verbose_canvas_cursor_with (buf
);
2911 MidiRegionView::drop_down_keys ()
2913 _mouse_state
= None
;
2917 MidiRegionView::maybe_select_by_position (GdkEventButton
* ev
, double x
, double y
)
2919 double note
= midi_stream_view()->y_to_note(y
);
2921 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
2923 cerr
<< "Selecting by position\n";
2925 uint16_t chn_mask
= mtv
->channel_selector().get_selected_channels();
2927 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::TertiaryModifier
)) {
2928 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchGreaterThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
2929 } else if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
2930 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchLessThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
2935 bool add_mrv_selection
= false;
2937 if (_selection
.empty()) {
2938 add_mrv_selection
= true;
2941 for (Events::iterator i
= e
.begin(); i
!= e
.end(); ++i
) {
2942 if (_selection
.insert (*i
).second
) {
2943 (*i
)->set_selected (true);
2947 if (add_mrv_selection
) {
2948 PublicEditor
& editor (trackview
.editor());
2949 editor
.get_selection().add (this);
2954 MidiRegionView::color_handler ()
2956 RegionView::color_handler ();
2958 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2959 (*i
)->set_selected ((*i
)->selected()); // will change color
2962 /* XXX probably more to do here */
2966 MidiRegionView::enable_display (bool yn
)
2968 RegionView::enable_display (yn
);