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/MIDIParameters.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "automation_region_view.h"
48 #include "automation_time_axis.h"
49 #include "canvas-hit.h"
50 #include "canvas-note.h"
51 #include "canvas_patch_change.h"
54 #include "ghostregion.h"
55 #include "gui_thread.h"
57 #include "midi_cut_buffer.h"
58 #include "midi_list_editor.h"
59 #include "midi_region_view.h"
60 #include "midi_streamview.h"
61 #include "midi_time_axis.h"
62 #include "midi_util.h"
63 #include "note_player.h"
64 #include "public_editor.h"
65 #include "rgb_macros.h"
66 #include "selection.h"
67 #include "simpleline.h"
68 #include "streamview.h"
70 #include "mouse_cursors.h"
71 #include "patch_change_dialog.h"
75 using namespace ARDOUR
;
77 using namespace Editing
;
78 using namespace ArdourCanvas
;
79 using Gtkmm2ext::Keyboard
;
81 MidiRegionView::MidiRegionView (ArdourCanvas::Group
*parent
, RouteTimeAxisView
&tv
,
82 boost::shared_ptr
<MidiRegion
> r
, double spu
, Gdk::Color
const & basic_color
)
83 : RegionView (parent
, tv
, r
, spu
, basic_color
)
85 , _last_channel_selection(0xFFFF)
86 , _current_range_min(0)
87 , _current_range_max(0)
88 , _model_name(string())
89 , _custom_device_mode(string())
91 , _note_group(new ArdourCanvas::Group(*group
))
92 , _note_diff_command (0)
95 , _step_edit_cursor (0)
96 , _step_edit_cursor_width (1.0)
97 , _step_edit_cursor_position (0.0)
98 , _channel_selection_scoped_note (0)
99 , _temporary_note_group (0)
102 , _sort_needed (true)
103 , _optimization_iterator (_events
.end())
105 , no_sound_notes (false)
108 , pre_enter_cursor (0)
110 _note_group
->raise_to_top();
111 PublicEditor::DropDownKeys
.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys
));
113 connect_to_diskstream ();
116 MidiRegionView::MidiRegionView (ArdourCanvas::Group
*parent
, RouteTimeAxisView
&tv
,
117 boost::shared_ptr
<MidiRegion
> r
, double spu
, Gdk::Color
& basic_color
,
118 TimeAxisViewItem::Visibility visibility
)
119 : RegionView (parent
, tv
, r
, spu
, basic_color
, false, visibility
)
121 , _last_channel_selection(0xFFFF)
122 , _model_name(string())
123 , _custom_device_mode(string())
125 , _note_group(new ArdourCanvas::Group(*parent
))
126 , _note_diff_command (0)
129 , _step_edit_cursor (0)
130 , _step_edit_cursor_width (1.0)
131 , _step_edit_cursor_position (0.0)
132 , _channel_selection_scoped_note (0)
133 , _temporary_note_group (0)
136 , _sort_needed (true)
137 , _optimization_iterator (_events
.end())
139 , no_sound_notes (false)
143 _note_group
->raise_to_top();
144 PublicEditor::DropDownKeys
.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys
));
146 connect_to_diskstream ();
149 MidiRegionView::MidiRegionView (const MidiRegionView
& other
)
150 : sigc::trackable(other
)
153 , _last_channel_selection(0xFFFF)
154 , _model_name(string())
155 , _custom_device_mode(string())
157 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
158 , _note_diff_command (0)
161 , _step_edit_cursor (0)
162 , _step_edit_cursor_width (1.0)
163 , _step_edit_cursor_position (0.0)
164 , _channel_selection_scoped_note (0)
165 , _temporary_note_group (0)
168 , _sort_needed (true)
169 , _optimization_iterator (_events
.end())
171 , no_sound_notes (false)
178 UINT_TO_RGBA (other
.fill_color
, &r
, &g
, &b
, &a
);
179 c
.set_rgb_p (r
/255.0, g
/255.0, b
/255.0);
184 MidiRegionView::MidiRegionView (const MidiRegionView
& other
, boost::shared_ptr
<MidiRegion
> region
)
185 : RegionView (other
, boost::shared_ptr
<Region
> (region
))
187 , _last_channel_selection(0xFFFF)
188 , _model_name(string())
189 , _custom_device_mode(string())
191 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
192 , _note_diff_command (0)
195 , _step_edit_cursor (0)
196 , _step_edit_cursor_width (1.0)
197 , _step_edit_cursor_position (0.0)
198 , _channel_selection_scoped_note (0)
199 , _temporary_note_group (0)
202 , _sort_needed (true)
203 , _optimization_iterator (_events
.end())
205 , no_sound_notes (false)
212 UINT_TO_RGBA (other
.fill_color
, &r
, &g
, &b
, &a
);
213 c
.set_rgb_p (r
/255.0, g
/255.0, b
/255.0);
219 MidiRegionView::init (Gdk::Color
const & basic_color
, bool wfd
)
221 PublicEditor::DropDownKeys
.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys
));
223 CanvasNoteEvent::CanvasNoteEventDeleted
.connect (note_delete_connection
, MISSING_INVALIDATOR
,
224 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection
, this, _1
),
228 midi_region()->midi_source(0)->load_model();
231 _model
= midi_region()->midi_source(0)->model();
232 _enable_display
= false;
234 RegionView::init (basic_color
, false);
236 compute_colors (basic_color
);
238 set_height (trackview
.current_height());
241 region_sync_changed ();
242 region_resized (ARDOUR::bounds_change
);
245 reset_width_dependent_items (_pixel_width
);
249 _enable_display
= true;
252 display_model (_model
);
256 group
->raise_to_top();
257 group
->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event
), false);
259 midi_view()->signal_channel_mode_changed().connect(
260 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed
));
262 midi_view()->signal_midi_patch_settings_changed().connect(
263 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed
));
265 trackview
.editor().SnapChanged
.connect (snap_changed_connection
, invalidator (*this), ui_bind (&MidiRegionView::snap_changed
, this), gui_context ());
267 connect_to_diskstream ();
271 MidiRegionView::connect_to_diskstream ()
273 midi_view()->midi_track()->DataRecorded
.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::data_recorded
, this, _1
, _2
), gui_context ());
277 MidiRegionView::canvas_event(GdkEvent
* ev
)
280 case GDK_ENTER_NOTIFY
:
281 case GDK_LEAVE_NOTIFY
:
282 _last_event_x
= ev
->crossing
.x
;
283 _last_event_y
= ev
->crossing
.y
;
285 case GDK_MOTION_NOTIFY
:
286 _last_event_x
= ev
->motion
.x
;
287 _last_event_y
= ev
->motion
.y
;
293 if (!trackview
.editor().internal_editing()) {
299 return scroll (&ev
->scroll
);
302 return key_press (&ev
->key
);
304 case GDK_KEY_RELEASE
:
305 return key_release (&ev
->key
);
307 case GDK_BUTTON_PRESS
:
308 return button_press (&ev
->button
);
310 case GDK_2BUTTON_PRESS
:
313 case GDK_BUTTON_RELEASE
:
314 return button_release (&ev
->button
);
316 case GDK_ENTER_NOTIFY
:
317 return enter_notify (&ev
->crossing
);
319 case GDK_LEAVE_NOTIFY
:
320 return leave_notify (&ev
->crossing
);
322 case GDK_MOTION_NOTIFY
:
323 return motion (&ev
->motion
);
333 MidiRegionView::remove_ghost_note ()
340 MidiRegionView::enter_notify (GdkEventCrossing
* ev
)
342 trackview
.editor().MouseModeChanged
.connect (
343 _mouse_mode_connection
, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed
, this), gui_context ()
346 Keyboard::magic_widget_grab_focus();
349 if (trackview
.editor().current_mouse_mode() == MouseRange
) {
350 create_ghost_note (ev
->x
, ev
->y
);
357 MidiRegionView::leave_notify (GdkEventCrossing
*)
359 _mouse_mode_connection
.disconnect ();
361 trackview
.editor().hide_verbose_canvas_cursor ();
362 remove_ghost_note ();
367 MidiRegionView::mouse_mode_changed ()
369 if (trackview
.editor().current_mouse_mode() == MouseRange
&& trackview
.editor().internal_editing()) {
370 create_ghost_note (_last_event_x
, _last_event_y
);
372 remove_ghost_note ();
373 trackview
.editor().hide_verbose_canvas_cursor ();
378 MidiRegionView::button_press (GdkEventButton
* ev
)
383 group
->w2i (_last_x
, _last_y
);
385 if (_mouse_state
!= SelectTouchDragging
) {
387 _pressed_button
= ev
->button
;
388 _mouse_state
= Pressed
;
393 _pressed_button
= ev
->button
;
399 MidiRegionView::button_release (GdkEventButton
* ev
)
401 double event_x
, event_y
;
402 framepos_t event_frame
= 0;
407 group
->w2i(event_x
, event_y
);
408 group
->ungrab(ev
->time
);
410 event_frame
= trackview
.editor().pixel_to_frame(event_x
);
412 switch (_mouse_state
) {
413 case Pressed
: // Clicked
415 switch (trackview
.editor().current_mouse_mode()) {
421 if (Keyboard::is_insert_note_event(ev
)){
423 double event_x
, event_y
;
427 group
->w2i(event_x
, event_y
);
430 Evoral::MusicalTime beats
= trackview
.editor().get_grid_type_as_beats (success
, trackview
.editor().pixel_to_frame (event_x
));
436 create_note_at (event_x
, event_y
, beats
, true);
444 Evoral::MusicalTime beats
= trackview
.editor().get_grid_type_as_beats (success
, trackview
.editor().pixel_to_frame (event_x
));
450 create_note_at (event_x
, event_y
, beats
, true);
461 case SelectRectDragging
: // Select drag done
468 case AddDragging
: // Add drag done
472 if (Keyboard::is_insert_note_event(ev
) || trackview
.editor().current_mouse_mode() == MouseRange
){
474 if (_drag_rect
->property_x2() > _drag_rect
->property_x1() + 2) {
476 const double x
= _drag_rect
->property_x1();
477 const double length
= trackview
.editor().pixel_to_frame (_drag_rect
->property_x2() - _drag_rect
->property_x1());
479 create_note_at (x
, _drag_rect
->property_y1(), frames_to_beats(length
), true);
486 create_ghost_note (ev
->x
, ev
->y
);
496 MidiRegionView::motion (GdkEventMotion
* ev
)
498 double event_x
, event_y
;
499 framepos_t event_frame
= 0;
503 group
->w2i(event_x
, event_y
);
505 // convert event_x to global frame
506 event_frame
= trackview
.editor().pixel_to_frame(event_x
) + _region
->position();
507 trackview
.editor().snap_to(event_frame
);
509 // convert event_frame back to local coordinates relative to position
510 event_frame
-= _region
->position();
512 if (!_ghost_note
&& trackview
.editor().current_mouse_mode() != MouseRange
513 && Keyboard::modifier_state_contains (ev
->state
, Keyboard::insert_note_modifier())
514 && _mouse_state
!= AddDragging
){
516 create_ghost_note (ev
->x
, ev
->y
);
518 else if (_ghost_note
&& trackview
.editor().current_mouse_mode() != MouseRange
519 && Keyboard::modifier_state_contains (ev
->state
, Keyboard::insert_note_modifier())){
521 update_ghost_note (ev
->x
, ev
->y
);
523 else if (_ghost_note
&& trackview
.editor().current_mouse_mode() != MouseRange
){
528 trackview
.editor().hide_verbose_canvas_cursor ();
530 else if (_ghost_note
&& trackview
.editor().current_mouse_mode() == MouseRange
) {
531 update_ghost_note (ev
->x
, ev
->y
);
534 /* any motion immediately hides velocity text that may have been visible */
536 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
537 (*i
)->hide_velocity ();
540 switch (_mouse_state
) {
541 case Pressed
: // Maybe start a drag, if we've moved a bit
543 if (fabs (event_x
- _last_x
) < 1 && fabs (event_y
- _last_y
) < 1) {
544 /* no appreciable movement since the button was pressed */
549 if (_pressed_button
== 1 && trackview
.editor().current_mouse_mode() == MouseObject
550 && !Keyboard::modifier_state_contains (ev
->state
, Keyboard::insert_note_modifier())) {
552 group
->grab(GDK_POINTER_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
,
553 Gdk::Cursor(Gdk::FLEUR
), ev
->time
);
557 _drag_start_x
= event_x
;
558 _drag_start_y
= event_y
;
560 _drag_rect
= new ArdourCanvas::SimpleRect(*group
);
561 _drag_rect
->property_x1() = event_x
;
562 _drag_rect
->property_y1() = event_y
;
563 _drag_rect
->property_x2() = event_x
;
564 _drag_rect
->property_y2() = event_y
;
565 _drag_rect
->property_outline_what() = 0xFF;
566 _drag_rect
->property_outline_color_rgba()
567 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline
.get();
568 _drag_rect
->property_fill_color_rgba()
569 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill
.get();
571 _mouse_state
= SelectRectDragging
;
574 // Add note drag start
575 } else if (trackview
.editor().internal_editing()) {
580 group
->grab(GDK_POINTER_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
,
581 Gdk::Cursor(Gdk::FLEUR
), ev
->time
);
585 _drag_start_x
= event_x
;
586 _drag_start_y
= event_y
;
588 _drag_rect
= new ArdourCanvas::SimpleRect(*group
);
589 _drag_rect
->property_x1() = trackview
.editor().frame_to_pixel(event_frame
);
591 _drag_rect
->property_y1() = midi_stream_view()->note_to_y(
592 midi_stream_view()->y_to_note(event_y
));
593 _drag_rect
->property_x2() = trackview
.editor().frame_to_pixel(event_frame
);
594 _drag_rect
->property_y2() = _drag_rect
->property_y1()
595 + floor(midi_stream_view()->note_height());
596 _drag_rect
->property_outline_what() = 0xFF;
597 _drag_rect
->property_outline_color_rgba() = 0xFFFFFF99;
598 _drag_rect
->property_fill_color_rgba() = 0xFFFFFF66;
600 _mouse_state
= AddDragging
;
607 trackview
.editor().hide_verbose_canvas_cursor ();
615 case SelectRectDragging
: // Select drag motion
616 case AddDragging
: // Add note drag motion
621 GdkModifierType state
;
622 gdk_window_get_pointer(ev
->window
, &t_x
, &t_y
, &state
);
627 if (_mouse_state
== AddDragging
){
628 event_x
= trackview
.editor().frame_to_pixel(event_frame
);
633 if (event_x
> _drag_start_x
){
634 _drag_rect
->property_x2() = event_x
;
637 _drag_rect
->property_x1() = event_x
;
641 if (_drag_rect
&& _mouse_state
== SelectRectDragging
) {
643 if (event_y
> _drag_start_y
){
644 _drag_rect
->property_y2() = event_y
;
647 _drag_rect
->property_y1() = event_y
;
650 update_drag_selection(_drag_start_x
, event_x
, _drag_start_y
, event_y
);
656 case SelectTouchDragging
:
668 MidiRegionView::scroll (GdkEventScroll
* ev
)
670 if (_selection
.empty()) {
674 trackview
.editor().hide_verbose_canvas_cursor ();
676 bool fine
= !Keyboard::modifier_state_equals (ev
->state
, Keyboard::SecondaryModifier
);
678 if (ev
->direction
== GDK_SCROLL_UP
) {
679 change_velocities (true, fine
, false);
680 } else if (ev
->direction
== GDK_SCROLL_DOWN
) {
681 change_velocities (false, fine
, false);
687 MidiRegionView::key_press (GdkEventKey
* ev
)
689 /* since GTK bindings are generally activated on press, and since
690 detectable auto-repeat is the name of the game and only sends
691 repeated presses, carry out key actions at key press, not release.
694 if (ev
->keyval
== GDK_Alt_L
|| ev
->keyval
== GDK_Alt_R
){
695 _mouse_state
= SelectTouchDragging
;
698 } else if (ev
->keyval
== GDK_Escape
) {
702 } else if (ev
->keyval
== GDK_comma
|| ev
->keyval
== GDK_period
) {
704 bool start
= (ev
->keyval
== GDK_comma
);
705 bool end
= (ev
->keyval
== GDK_period
);
706 bool shorter
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
);
707 bool fine
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
709 change_note_lengths (fine
, shorter
, 0.0, start
, end
);
713 } else if (ev
->keyval
== GDK_Delete
) {
718 } else if (ev
->keyval
== GDK_Tab
) {
720 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
721 goto_previous_note ();
727 } else if (ev
->keyval
== GDK_Up
) {
729 bool allow_smush
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::TertiaryModifier
);
730 bool fine
= !Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
732 if (Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
)) {
733 change_velocities (true, fine
, allow_smush
);
735 transpose (true, fine
, allow_smush
);
739 } else if (ev
->keyval
== GDK_Down
) {
741 bool allow_smush
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::TertiaryModifier
);
742 bool fine
= !Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
744 if (Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
)) {
745 change_velocities (false, fine
, allow_smush
);
747 transpose (false, fine
, allow_smush
);
751 } else if (ev
->keyval
== GDK_Left
) {
756 } else if (ev
->keyval
== GDK_Right
) {
761 } else if (ev
->keyval
== GDK_Control_L
) {
770 MidiRegionView::key_release (GdkEventKey
* ev
)
772 if (ev
->keyval
== GDK_Alt_L
|| ev
->keyval
== GDK_Alt_R
) {
780 MidiRegionView::show_list_editor ()
783 _list_editor
= new MidiListEditor (trackview
.session(), midi_region());
785 _list_editor
->present ();
788 /** Add a note to the model, and the view, at a canvas (click) coordinate.
789 * \param x horizontal position in pixels
790 * \param y vertical position in pixels
791 * \param length duration of the note in beats, which will be snapped to the grid
792 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
795 MidiRegionView::create_note_at(double x
, double y
, double length
, bool sh
)
797 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
798 MidiStreamView
* const view
= mtv
->midi_view();
800 double note
= view
->y_to_note(y
);
803 assert(note
<= 127.0);
805 // Start of note in frames relative to region start
806 framepos_t
const start_frames
= snap_frame_to_frame(trackview
.editor().pixel_to_frame(x
));
807 assert(start_frames
>= 0);
810 length
= frames_to_beats(
811 snap_frame_to_frame(start_frames
+ beats_to_frames(length
)) - start_frames
);
813 assert (length
!= 0);
816 length
= frames_to_beats (beats_to_frames (length
) - 1);
819 const boost::shared_ptr
<NoteType
> new_note (new NoteType (get_channel_for_add (),
820 frames_to_beats(start_frames
+ _region
->start()), length
,
821 (uint8_t)note
, 0x40));
823 if (_model
->contains (new_note
)) {
827 view
->update_note_range(new_note
->note());
829 MidiModel::NoteDiffCommand
* cmd
= _model
->new_note_diff_command("add note");
831 _model
->apply_command(*trackview
.session(), cmd
);
833 play_midi_note (new_note
);
837 MidiRegionView::clear_events()
842 for (std::vector
<GhostRegion
*>::iterator g
= ghosts
.begin(); g
!= ghosts
.end(); ++g
) {
843 if ((gr
= dynamic_cast<MidiGhostRegion
*>(*g
)) != 0) {
848 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
853 _patch_changes
.clear();
855 _optimization_iterator
= _events
.end();
859 MidiRegionView::display_model(boost::shared_ptr
<MidiModel
> model
)
863 content_connection
.disconnect ();
864 _model
->ContentsChanged
.connect (content_connection
, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model
, this), gui_context());
868 if (_enable_display
) {
874 MidiRegionView::start_note_diff_command (string name
)
876 if (!_note_diff_command
) {
877 _note_diff_command
= _model
->new_note_diff_command (name
);
882 MidiRegionView::note_diff_add_note (const boost::shared_ptr
<NoteType
> note
, bool selected
, bool show_velocity
)
884 if (_note_diff_command
) {
885 _note_diff_command
->add (note
);
888 _marked_for_selection
.insert(note
);
891 _marked_for_velocity
.insert(note
);
896 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent
* ev
)
898 if (_note_diff_command
&& ev
->note()) {
899 _note_diff_command
->remove(ev
->note());
904 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent
* ev
,
905 MidiModel::NoteDiffCommand::Property property
,
908 if (_note_diff_command
) {
909 _note_diff_command
->change (ev
->note(), property
, val
);
914 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent
* ev
,
915 MidiModel::NoteDiffCommand::Property property
,
916 Evoral::MusicalTime val
)
918 if (_note_diff_command
) {
919 _note_diff_command
->change (ev
->note(), property
, val
);
924 MidiRegionView::apply_diff ()
928 if (!_note_diff_command
) {
932 if ((add_or_remove
= _note_diff_command
->adds_or_removes())) {
933 // Mark all selected notes for selection when model reloads
934 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
935 _marked_for_selection
.insert((*i
)->note());
939 _model
->apply_command(*trackview
.session(), _note_diff_command
);
940 _note_diff_command
= 0;
941 midi_view()->midi_track()->playlist_modified();
944 _marked_for_selection
.clear();
947 _marked_for_velocity
.clear();
951 MidiRegionView::apply_diff_as_subcommand ()
955 if (!_note_diff_command
) {
959 if ((add_or_remove
= _note_diff_command
->adds_or_removes())) {
960 // Mark all selected notes for selection when model reloads
961 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
962 _marked_for_selection
.insert((*i
)->note());
966 _model
->apply_command_as_subcommand(*trackview
.session(), _note_diff_command
);
967 _note_diff_command
= 0;
968 midi_view()->midi_track()->playlist_modified();
971 _marked_for_selection
.clear();
973 _marked_for_velocity
.clear();
978 MidiRegionView::abort_command()
980 delete _note_diff_command
;
981 _note_diff_command
= 0;
986 MidiRegionView::find_canvas_note (boost::shared_ptr
<NoteType
> note
)
988 if (_optimization_iterator
!= _events
.end()) {
989 ++_optimization_iterator
;
992 if (_optimization_iterator
!= _events
.end() && (*_optimization_iterator
)->note() == note
) {
993 return *_optimization_iterator
;
996 for (_optimization_iterator
= _events
.begin(); _optimization_iterator
!= _events
.end(); ++_optimization_iterator
) {
997 if ((*_optimization_iterator
)->note() == note
) {
998 return *_optimization_iterator
;
1006 MidiRegionView::get_events (Events
& e
, Evoral::Sequence
<Evoral::MusicalTime
>::NoteOperator op
, uint8_t val
, int chan_mask
)
1008 MidiModel::Notes notes
;
1009 _model
->get_notes (notes
, op
, val
, chan_mask
);
1011 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1012 CanvasNoteEvent
* cne
= find_canvas_note (*n
);
1020 MidiRegionView::redisplay_model()
1022 // Don't redisplay the model if we're currently recording and displaying that
1023 if (_active_notes
) {
1028 cerr
<< "MidiRegionView::redisplay_model called without a model" << endmsg
;
1032 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1033 (*i
)->invalidate ();
1036 MidiModel::ReadLock
lock(_model
->read_lock());
1038 MidiModel::Notes
& notes (_model
->notes());
1039 _optimization_iterator
= _events
.begin();
1041 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1043 boost::shared_ptr
<NoteType
> note (*n
);
1044 CanvasNoteEvent
* cne
;
1047 if (note_in_region_range (note
, visible
)) {
1049 if ((cne
= find_canvas_note (note
)) != 0) {
1056 if ((cn
= dynamic_cast<CanvasNote
*>(cne
)) != 0) {
1058 } else if ((ch
= dynamic_cast<CanvasHit
*>(cne
)) != 0) {
1070 add_note (note
, visible
);
1075 if ((cne
= find_canvas_note (note
)) != 0) {
1083 /* remove note items that are no longer valid */
1085 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ) {
1086 if (!(*i
)->valid ()) {
1088 i
= _events
.erase (i
);
1094 _patch_changes
.clear();
1098 display_patch_changes ();
1100 _marked_for_selection
.clear ();
1101 _marked_for_velocity
.clear ();
1103 /* we may have caused _events to contain things out of order (e.g. if a note
1104 moved earlier or later). we don't generally need them in time order, but
1105 make a note that a sort is required for those cases that require it.
1108 _sort_needed
= true;
1112 MidiRegionView::display_patch_changes ()
1114 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
1115 uint16_t chn_mask
= mtv
->channel_selector().get_selected_channels();
1117 for (uint8_t i
= 0; i
< 16; ++i
) {
1118 if (chn_mask
& (1<<i
)) {
1119 display_patch_changes_on_channel (i
);
1125 MidiRegionView::display_patch_changes_on_channel (uint8_t channel
)
1127 for (MidiModel::PatchChanges::const_iterator i
= _model
->patch_changes().begin(); i
!= _model
->patch_changes().end(); ++i
) {
1129 if ((*i
)->channel() != channel
) {
1133 MIDI::Name::PatchPrimaryKey
patch_key ((*i
)->bank_msb (), (*i
)->bank_lsb (), (*i
)->program ());
1135 boost::shared_ptr
<MIDI::Name::Patch
> patch
=
1136 MIDI::Name::MidiPatchManager::instance().find_patch(
1137 _model_name
, _custom_device_mode
, channel
, patch_key
);
1140 add_canvas_patch_change (*i
, patch
->name());
1143 /* program and bank numbers are zero-based: convert to one-based */
1144 snprintf (buf
, 16, "%d\n%d", (*i
)->program() + 1, (*i
)->bank() + 1);
1145 add_canvas_patch_change (*i
, buf
);
1151 MidiRegionView::display_sysexes()
1153 for (MidiModel::SysExes::const_iterator i
= _model
->sysexes().begin(); i
!= _model
->sysexes().end(); ++i
) {
1154 Evoral::MusicalTime time
= (*i
)->time();
1159 for (uint32_t b
= 0; b
< (*i
)->size(); ++b
) {
1160 str
<< int((*i
)->buffer()[b
]);
1161 if (b
!= (*i
)->size() -1) {
1165 string text
= str
.str();
1167 const double x
= trackview
.editor().frame_to_pixel(beats_to_frames(time
));
1169 double height
= midi_stream_view()->contents_height();
1171 boost::shared_ptr
<CanvasSysEx
> sysex
= boost::shared_ptr
<CanvasSysEx
>(
1172 new CanvasSysEx(*this, *_note_group
, text
, height
, x
, 1.0));
1174 // Show unless patch change is beyond the region bounds
1175 if (time
- _region
->start() >= _region
->length() || time
< _region
->start()) {
1181 _sys_exes
.push_back(sysex
);
1186 MidiRegionView::~MidiRegionView ()
1188 in_destructor
= true;
1190 trackview
.editor().hide_verbose_canvas_cursor ();
1192 note_delete_connection
.disconnect ();
1194 delete _list_editor
;
1196 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1198 if (_active_notes
) {
1206 delete _note_diff_command
;
1207 delete _step_edit_cursor
;
1208 delete _temporary_note_group
;
1212 MidiRegionView::region_resized (const PropertyChange
& what_changed
)
1214 RegionView::region_resized(what_changed
);
1216 if (what_changed
.contains (ARDOUR::Properties::position
)) {
1217 set_duration(_region
->length(), 0);
1218 if (_enable_display
) {
1225 MidiRegionView::reset_width_dependent_items (double pixel_width
)
1227 RegionView::reset_width_dependent_items(pixel_width
);
1228 assert(_pixel_width
== pixel_width
);
1230 if (_enable_display
) {
1234 move_step_edit_cursor (_step_edit_cursor_position
);
1235 set_step_edit_cursor_width (_step_edit_cursor_width
);
1239 MidiRegionView::set_height (double height
)
1241 static const double FUDGE
= 2.0;
1242 const double old_height
= _height
;
1243 RegionView::set_height(height
);
1244 _height
= height
- FUDGE
;
1246 apply_note_range(midi_stream_view()->lowest_note(),
1247 midi_stream_view()->highest_note(),
1248 height
!= old_height
+ FUDGE
);
1251 name_pixbuf
->raise_to_top();
1254 for (PatchChanges::iterator x
= _patch_changes
.begin(); x
!= _patch_changes
.end(); ++x
) {
1255 (*x
)->set_height (midi_stream_view()->contents_height());
1258 if (_step_edit_cursor
) {
1259 _step_edit_cursor
->property_y2() = midi_stream_view()->contents_height();
1264 /** Apply the current note range from the stream view
1265 * by repositioning/hiding notes as necessary
1268 MidiRegionView::apply_note_range (uint8_t min
, uint8_t max
, bool force
)
1270 if (!_enable_display
) {
1274 if (!force
&& _current_range_min
== min
&& _current_range_max
== max
) {
1278 _current_range_min
= min
;
1279 _current_range_max
= max
;
1281 for (Events::const_iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1282 CanvasNoteEvent
* event
= *i
;
1283 boost::shared_ptr
<NoteType
> note (event
->note());
1285 if (note
->note() < _current_range_min
||
1286 note
->note() > _current_range_max
) {
1292 if (CanvasNote
* cnote
= dynamic_cast<CanvasNote
*>(event
)) {
1294 const double y1
= midi_stream_view()->note_to_y(note
->note());
1295 const double y2
= y1
+ floor(midi_stream_view()->note_height());
1297 cnote
->property_y1() = y1
;
1298 cnote
->property_y2() = y2
;
1300 } else if (CanvasHit
* chit
= dynamic_cast<CanvasHit
*>(event
)) {
1302 const double diamond_size
= update_hit (chit
);
1304 chit
->set_height (diamond_size
);
1310 MidiRegionView::add_ghost (TimeAxisView
& tv
)
1314 double unit_position
= _region
->position () / samples_per_unit
;
1315 MidiTimeAxisView
* mtv
= dynamic_cast<MidiTimeAxisView
*>(&tv
);
1316 MidiGhostRegion
* ghost
;
1318 if (mtv
&& mtv
->midi_view()) {
1319 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1320 to allow having midi notes on top of note lines and waveforms.
1322 ghost
= new MidiGhostRegion (*mtv
->midi_view(), trackview
, unit_position
);
1324 ghost
= new MidiGhostRegion (tv
, trackview
, unit_position
);
1327 ghost
->set_height ();
1328 ghost
->set_duration (_region
->length() / samples_per_unit
);
1329 ghosts
.push_back (ghost
);
1331 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1332 if ((note
= dynamic_cast<CanvasNote
*>(*i
)) != 0) {
1333 ghost
->add_note(note
);
1337 GhostRegion::CatchDeletion
.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost
, this, _1
), gui_context());
1343 /** Begin tracking note state for successive calls to add_event
1346 MidiRegionView::begin_write()
1348 assert(!_active_notes
);
1349 _active_notes
= new CanvasNote
*[128];
1350 for (unsigned i
=0; i
< 128; ++i
) {
1351 _active_notes
[i
] = 0;
1356 /** Destroy note state for add_event
1359 MidiRegionView::end_write()
1361 delete[] _active_notes
;
1363 _marked_for_selection
.clear();
1364 _marked_for_velocity
.clear();
1368 /** Resolve an active MIDI note (while recording).
1371 MidiRegionView::resolve_note(uint8_t note
, double end_time
)
1373 if (midi_view()->note_mode() != Sustained
) {
1377 if (_active_notes
&& _active_notes
[note
]) {
1379 const framepos_t end_time_frames
= beats_to_frames(end_time
);
1381 _active_notes
[note
]->property_x2() = trackview
.editor().frame_to_pixel(end_time_frames
);
1382 _active_notes
[note
]->property_outline_what() = (guint32
) 0xF; // all edges
1383 _active_notes
[note
] = 0;
1388 /** Extend active notes to rightmost edge of region (if length is changed)
1391 MidiRegionView::extend_active_notes()
1393 if (!_active_notes
) {
1397 for (unsigned i
=0; i
< 128; ++i
) {
1398 if (_active_notes
[i
]) {
1399 _active_notes
[i
]->property_x2() = trackview
.editor().frame_to_pixel(_region
->length());
1406 MidiRegionView::play_midi_note(boost::shared_ptr
<NoteType
> note
)
1408 if (no_sound_notes
|| !trackview
.editor().sound_notes()) {
1412 RouteUI
* route_ui
= dynamic_cast<RouteUI
*> (&trackview
);
1414 if (!route_ui
|| !route_ui
->midi_track()) {
1418 NotePlayer
* np
= new NotePlayer (route_ui
->midi_track());
1424 MidiRegionView::play_midi_chord (vector
<boost::shared_ptr
<NoteType
> > notes
)
1426 if (no_sound_notes
|| !trackview
.editor().sound_notes()) {
1430 RouteUI
* route_ui
= dynamic_cast<RouteUI
*> (&trackview
);
1432 if (!route_ui
|| !route_ui
->midi_track()) {
1436 NotePlayer
* np
= new NotePlayer (route_ui
->midi_track());
1438 for (vector
<boost::shared_ptr
<NoteType
> >::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1447 MidiRegionView::note_in_region_range(const boost::shared_ptr
<NoteType
> note
, bool& visible
) const
1449 const framepos_t note_start_frames
= beats_to_frames(note
->time());
1451 bool outside
= (note_start_frames
- _region
->start() >= _region
->length()) ||
1452 (note_start_frames
< _region
->start());
1454 visible
= (note
->note() >= midi_stream_view()->lowest_note()) &&
1455 (note
->note() <= midi_stream_view()->highest_note());
1461 MidiRegionView::update_note (CanvasNote
* ev
)
1463 boost::shared_ptr
<NoteType
> note
= ev
->note();
1465 const framepos_t note_start_frames
= beats_to_frames(note
->time());
1467 /* trim note display to not overlap the end of its region */
1468 const framepos_t note_end_frames
= min (beats_to_frames (note
->end_time()), _region
->start() + _region
->length());
1470 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1471 const double y1
= midi_stream_view()->note_to_y(note
->note());
1472 const double note_endpixel
= trackview
.editor().frame_to_pixel(note_end_frames
- _region
->start());
1474 ev
->property_x1() = x
;
1475 ev
->property_y1() = y1
;
1477 if (note
->length() > 0) {
1478 ev
->property_x2() = note_endpixel
;
1480 ev
->property_x2() = trackview
.editor().frame_to_pixel(_region
->length());
1483 ev
->property_y2() = y1
+ floor(midi_stream_view()->note_height());
1485 if (note
->length() == 0) {
1486 if (_active_notes
) {
1487 assert(note
->note() < 128);
1488 // If this note is already active there's a stuck note,
1489 // finish the old note rectangle
1490 if (_active_notes
[note
->note()]) {
1491 CanvasNote
* const old_rect
= _active_notes
[note
->note()];
1492 boost::shared_ptr
<NoteType
> old_note
= old_rect
->note();
1493 old_rect
->property_x2() = x
;
1494 old_rect
->property_outline_what() = (guint32
) 0xF;
1496 _active_notes
[note
->note()] = ev
;
1498 /* outline all but right edge */
1499 ev
->property_outline_what() = (guint32
) (0x1 & 0x4 & 0x8);
1501 /* outline all edges */
1502 ev
->property_outline_what() = (guint32
) 0xF;
1507 MidiRegionView::update_hit (CanvasHit
* ev
)
1509 boost::shared_ptr
<NoteType
> note
= ev
->note();
1511 const framepos_t note_start_frames
= beats_to_frames(note
->time());
1512 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1513 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1514 const double y
= midi_stream_view()->note_to_y(note
->note()) + ((diamond_size
-2) / 4.0);
1518 return diamond_size
;
1521 /** Add a MIDI note to the view (with length).
1523 * If in sustained mode, notes with length 0 will be considered active
1524 * notes, and resolve_note should be called when the corresponding note off
1525 * event arrives, to properly display the note.
1528 MidiRegionView::add_note(const boost::shared_ptr
<NoteType
> note
, bool visible
)
1530 CanvasNoteEvent
* event
= 0;
1532 assert(note
->time() >= 0);
1533 assert(midi_view()->note_mode() == Sustained
|| midi_view()->note_mode() == Percussive
);
1535 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1537 if (midi_view()->note_mode() == Sustained
) {
1539 CanvasNote
* ev_rect
= new CanvasNote(*this, *_note_group
, note
);
1541 update_note (ev_rect
);
1545 MidiGhostRegion
* gr
;
1547 for (std::vector
<GhostRegion
*>::iterator g
= ghosts
.begin(); g
!= ghosts
.end(); ++g
) {
1548 if ((gr
= dynamic_cast<MidiGhostRegion
*>(*g
)) != 0) {
1549 gr
->add_note(ev_rect
);
1553 } else if (midi_view()->note_mode() == Percussive
) {
1555 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1557 CanvasHit
* ev_diamond
= new CanvasHit(*this, *_note_group
, diamond_size
, note
);
1559 update_hit (ev_diamond
);
1568 if (_marked_for_selection
.find(note
) != _marked_for_selection
.end()) {
1569 note_selected(event
, true);
1572 if (_marked_for_velocity
.find(note
) != _marked_for_velocity
.end()) {
1573 event
->show_velocity();
1576 event
->on_channel_selection_change(_last_channel_selection
);
1577 _events
.push_back(event
);
1586 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
1587 MidiStreamView
* const view
= mtv
->midi_view();
1589 view
->update_note_range(note
->note());
1593 MidiRegionView::step_add_note (uint8_t channel
, uint8_t number
, uint8_t velocity
,
1594 Evoral::MusicalTime pos
, Evoral::MusicalTime len
)
1596 boost::shared_ptr
<NoteType
> new_note (new NoteType (channel
, pos
, len
, number
, velocity
));
1598 /* potentially extend region to hold new note */
1600 framepos_t end_frame
= _region
->position() + beats_to_frames (new_note
->end_time());
1601 framepos_t region_end
= _region
->position() + _region
->length() - 1;
1603 if (end_frame
> region_end
) {
1604 _region
->set_length (end_frame
- _region
->position(), this);
1607 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
1608 MidiStreamView
* const view
= mtv
->midi_view();
1610 view
->update_note_range(new_note
->note());
1612 _marked_for_selection
.clear ();
1615 start_note_diff_command (_("step add"));
1616 note_diff_add_note (new_note
, true, false);
1619 // last_step_edit_note = new_note;
1623 MidiRegionView::step_sustain (Evoral::MusicalTime beats
)
1625 change_note_lengths (false, false, beats
, false, true);
1629 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch
, const string
& displaytext
)
1631 assert (patch
->time() >= 0);
1633 const double x
= trackview
.editor().frame_to_pixel (beats_to_frames (patch
->time()));
1635 double const height
= midi_stream_view()->contents_height();
1637 boost::shared_ptr
<CanvasPatchChange
> patch_change
= boost::shared_ptr
<CanvasPatchChange
>(
1638 new CanvasPatchChange(*this, *_note_group
,
1643 _custom_device_mode
,
1647 // Show unless patch change is beyond the region bounds
1648 if (patch
->time() - _region
->start() >= _region
->length() || patch
->time() < _region
->start()) {
1649 patch_change
->hide();
1651 patch_change
->show();
1654 _patch_changes
.push_back (patch_change
);
1658 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time
, uint8_t channel
, MIDI::Name::PatchPrimaryKey
& key
)
1660 MidiModel::PatchChanges::iterator i
= _model
->patch_change_lower_bound (time
);
1661 while (i
!= _model
->patch_changes().end() && (*i
)->channel() != channel
) {
1665 if (i
!= _model
->patch_changes().end()) {
1666 key
.msb
= (*i
)->bank_msb ();
1667 key
.lsb
= (*i
)->bank_lsb ();
1668 key
.program_number
= (*i
)->program ();
1670 key
.msb
= key
.lsb
= key
.program_number
= 0;
1673 assert (key
.is_sane());
1678 MidiRegionView::change_patch_change (CanvasPatchChange
& pc
, const MIDI::Name::PatchPrimaryKey
& new_patch
)
1680 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("alter patch change"));
1682 if (pc
.patch()->program() != new_patch
.program_number
) {
1683 c
->change_program (pc
.patch (), new_patch
.program_number
);
1686 int const new_bank
= (new_patch
.msb
<< 7) | new_patch
.lsb
;
1687 if (pc
.patch()->bank() != new_bank
) {
1688 c
->change_bank (pc
.patch (), new_bank
);
1691 _model
->apply_command (*trackview
.session(), c
);
1693 _patch_changes
.clear ();
1694 display_patch_changes ();
1698 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change
, const Evoral::PatchChange
<Evoral::MusicalTime
> & new_change
)
1700 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("alter patch change"));
1702 if (old_change
->time() != new_change
.time()) {
1703 c
->change_time (old_change
, new_change
.time());
1706 if (old_change
->channel() != new_change
.channel()) {
1707 c
->change_channel (old_change
, new_change
.channel());
1710 if (old_change
->program() != new_change
.program()) {
1711 c
->change_program (old_change
, new_change
.program());
1714 if (old_change
->bank() != new_change
.bank()) {
1715 c
->change_bank (old_change
, new_change
.bank());
1718 _model
->apply_command (*trackview
.session(), c
);
1720 _patch_changes
.clear ();
1721 display_patch_changes ();
1724 /** Add a patch change to the region.
1725 * @param t Time in frames relative to region position
1726 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1727 * get_channel_for_add())
1730 MidiRegionView::add_patch_change (framecnt_t t
, Evoral::PatchChange
<Evoral::MusicalTime
> const & patch
)
1732 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("add patch change"));
1733 c
->add (MidiModel::PatchChangePtr (
1734 new Evoral::PatchChange
<Evoral::MusicalTime
> (
1735 frames_to_beats (t
+ midi_region()->start()), get_channel_for_add(), patch
.program(), patch
.bank()
1739 _model
->apply_command (*trackview
.session(), c
);
1741 _patch_changes
.clear ();
1742 display_patch_changes ();
1746 MidiRegionView::move_patch_change (CanvasPatchChange
& pc
, Evoral::MusicalTime t
)
1748 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("move patch change"));
1749 c
->change_time (pc
.patch (), t
);
1750 _model
->apply_command (*trackview
.session(), c
);
1752 _patch_changes
.clear ();
1753 display_patch_changes ();
1757 MidiRegionView::delete_patch_change (CanvasPatchChange
* pc
)
1759 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("delete patch change"));
1760 c
->remove (pc
->patch ());
1761 _model
->apply_command (*trackview
.session(), c
);
1763 _patch_changes
.clear ();
1764 display_patch_changes ();
1768 MidiRegionView::previous_patch (CanvasPatchChange
& patch
)
1770 if (patch
.patch()->program() < 127) {
1771 MIDI::Name::PatchPrimaryKey key
;
1772 get_patch_key_at (patch
.patch()->time(), patch
.patch()->channel(), key
);
1773 key
.program_number
++;
1774 change_patch_change (patch
, key
);
1779 MidiRegionView::next_patch (CanvasPatchChange
& patch
)
1781 if (patch
.patch()->program() > 0) {
1782 MIDI::Name::PatchPrimaryKey key
;
1783 get_patch_key_at (patch
.patch()->time(), patch
.patch()->channel(), key
);
1784 key
.program_number
--;
1785 change_patch_change (patch
, key
);
1790 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent
* cne
)
1792 if (_selection
.empty()) {
1796 if (_selection
.erase (cne
) > 0) {
1797 cerr
<< "Erased a CNE from selection\n";
1802 MidiRegionView::delete_selection()
1804 if (_selection
.empty()) {
1808 start_note_diff_command (_("delete selection"));
1810 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1811 if ((*i
)->selected()) {
1812 _note_diff_command
->remove((*i
)->note());
1822 MidiRegionView::delete_note (boost::shared_ptr
<NoteType
> n
)
1824 start_note_diff_command (_("delete note"));
1825 _note_diff_command
->remove (n
);
1828 trackview
.editor().hide_verbose_canvas_cursor ();
1832 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent
* ev
)
1834 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1835 if ((*i
)->selected() && (*i
) != ev
) {
1836 (*i
)->set_selected(false);
1837 (*i
)->hide_velocity();
1845 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent
* ev
)
1847 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
1850 Selection::iterator tmp
= i
;
1853 (*i
)->set_selected (false);
1854 _selection
.erase (i
);
1863 /* don't bother with removing this regionview from the editor selection,
1864 since we're about to add another note, and thus put/keep this
1865 regionview in the editor selection.
1868 if (!ev
->selected()) {
1869 add_to_selection (ev
);
1874 MidiRegionView::select_all_notes ()
1878 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1879 add_to_selection (*i
);
1884 MidiRegionView::select_matching_notes (uint8_t notenum
, uint16_t channel_mask
, bool add
, bool extend
)
1886 uint8_t low_note
= 127;
1887 uint8_t high_note
= 0;
1888 MidiModel::Notes
& notes (_model
->notes());
1889 _optimization_iterator
= _events
.begin();
1895 if (extend
&& _selection
.empty()) {
1901 /* scan existing selection to get note range */
1903 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1904 if ((*i
)->note()->note() < low_note
) {
1905 low_note
= (*i
)->note()->note();
1907 if ((*i
)->note()->note() > high_note
) {
1908 high_note
= (*i
)->note()->note();
1912 low_note
= min (low_note
, notenum
);
1913 high_note
= max (high_note
, notenum
);
1916 no_sound_notes
= true;
1918 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1920 boost::shared_ptr
<NoteType
> note (*n
);
1921 CanvasNoteEvent
* cne
;
1922 bool select
= false;
1924 if (((1 << note
->channel()) & channel_mask
) != 0) {
1926 if ((note
->note() >= low_note
&& note
->note() <= high_note
)) {
1929 } else if (note
->note() == notenum
) {
1935 if ((cne
= find_canvas_note (note
)) != 0) {
1936 // extend is false because we've taken care of it,
1937 // since it extends by time range, not pitch.
1938 note_selected (cne
, add
, false);
1942 add
= true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1946 no_sound_notes
= false;
1950 MidiRegionView::toggle_matching_notes (uint8_t notenum
, uint16_t channel_mask
)
1952 MidiModel::Notes
& notes (_model
->notes());
1953 _optimization_iterator
= _events
.begin();
1955 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1957 boost::shared_ptr
<NoteType
> note (*n
);
1958 CanvasNoteEvent
* cne
;
1960 if (note
->note() == notenum
&& (((0x0001 << note
->channel()) & channel_mask
) != 0)) {
1961 if ((cne
= find_canvas_note (note
)) != 0) {
1962 if (cne
->selected()) {
1963 note_deselected (cne
);
1965 note_selected (cne
, true, false);
1973 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent
* ev
, bool add
, bool extend
)
1976 clear_selection_except(ev
);
1981 if (!ev
->selected()) {
1982 add_to_selection (ev
);
1986 /* find end of latest note selected, select all between that and the start of "ev" */
1988 Evoral::MusicalTime earliest
= Evoral::MaxMusicalTime
;
1989 Evoral::MusicalTime latest
= 0;
1991 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1992 if ((*i
)->note()->end_time() > latest
) {
1993 latest
= (*i
)->note()->end_time();
1995 if ((*i
)->note()->time() < earliest
) {
1996 earliest
= (*i
)->note()->time();
2000 if (ev
->note()->end_time() > latest
) {
2001 latest
= ev
->note()->end_time();
2004 if (ev
->note()->time() < earliest
) {
2005 earliest
= ev
->note()->time();
2008 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2010 /* find notes entirely within OR spanning the earliest..latest range */
2012 if (((*i
)->note()->time() >= earliest
&& (*i
)->note()->end_time() <= latest
) ||
2013 ((*i
)->note()->time() <= earliest
&& (*i
)->note()->end_time() >= latest
)) {
2014 add_to_selection (*i
);
2018 /* if events were guaranteed to be time sorted, we could do this.
2019 but as of sept 10th 2009, they no longer are.
2022 if ((*i
)->note()->time() > latest
) {
2031 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent
* ev
)
2033 remove_from_selection (ev
);
2037 MidiRegionView::update_drag_selection(double x1
, double x2
, double y1
, double y2
)
2047 // TODO: Make this faster by storing the last updated selection rect, and only
2048 // adjusting things that are in the area that appears/disappeared.
2049 // We probably need a tree to be able to find events in O(log(n)) time.
2051 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2053 /* check if any corner of the note is inside the rect
2056 1) this is computing "touched by", not "contained by" the rect.
2057 2) this does not require that events be sorted in time.
2060 const double ix1
= (*i
)->x1();
2061 const double ix2
= (*i
)->x2();
2062 const double iy1
= (*i
)->y1();
2063 const double iy2
= (*i
)->y2();
2065 if ((ix1
>= x1
&& ix1
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
2066 (ix1
>= x1
&& ix1
<= x2
&& iy2
>= y1
&& iy2
<= y2
) ||
2067 (ix2
>= x1
&& ix2
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
2068 (ix2
>= x1
&& ix2
<= x2
&& iy2
>= y1
&& iy2
<= y2
)) {
2071 if (!(*i
)->selected()) {
2072 add_to_selection (*i
);
2074 } else if ((*i
)->selected()) {
2075 // Not inside rectangle
2076 remove_from_selection (*i
);
2082 MidiRegionView::remove_from_selection (CanvasNoteEvent
* ev
)
2084 Selection::iterator i
= _selection
.find (ev
);
2086 if (i
!= _selection
.end()) {
2087 _selection
.erase (i
);
2090 ev
->set_selected (false);
2091 ev
->hide_velocity ();
2093 if (_selection
.empty()) {
2094 PublicEditor
& editor (trackview
.editor());
2095 editor
.get_selection().remove (this);
2100 MidiRegionView::add_to_selection (CanvasNoteEvent
* ev
)
2102 bool add_mrv_selection
= false;
2104 if (_selection
.empty()) {
2105 add_mrv_selection
= true;
2108 if (_selection
.insert (ev
).second
) {
2109 ev
->set_selected (true);
2110 play_midi_note ((ev
)->note());
2113 if (add_mrv_selection
) {
2114 PublicEditor
& editor (trackview
.editor());
2115 editor
.get_selection().add (this);
2120 MidiRegionView::move_selection(double dx
, double dy
, double cumulative_dy
)
2122 typedef vector
<boost::shared_ptr
<NoteType
> > PossibleChord
;
2123 PossibleChord to_play
;
2124 Evoral::MusicalTime earliest
= Evoral::MaxMusicalTime
;
2126 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2127 if ((*i
)->note()->time() < earliest
) {
2128 earliest
= (*i
)->note()->time();
2132 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2133 if (Evoral::musical_time_equal ((*i
)->note()->time(), earliest
)) {
2134 to_play
.push_back ((*i
)->note());
2136 (*i
)->move_event(dx
, dy
);
2139 if (dy
&& !_selection
.empty() && !no_sound_notes
&& trackview
.editor().sound_notes()) {
2141 if (to_play
.size() > 1) {
2143 PossibleChord shifted
;
2145 for (PossibleChord::iterator n
= to_play
.begin(); n
!= to_play
.end(); ++n
) {
2146 boost::shared_ptr
<NoteType
> moved_note (new NoteType (**n
));
2147 moved_note
->set_note (moved_note
->note() + cumulative_dy
);
2148 shifted
.push_back (moved_note
);
2151 play_midi_chord (shifted
);
2153 } else if (!to_play
.empty()) {
2155 boost::shared_ptr
<NoteType
> moved_note (new NoteType (*to_play
.front()));
2156 moved_note
->set_note (moved_note
->note() + cumulative_dy
);
2157 play_midi_note (moved_note
);
2163 MidiRegionView::note_dropped(CanvasNoteEvent
*, frameoffset_t dt
, int8_t dnote
)
2165 assert (!_selection
.empty());
2167 uint8_t lowest_note_in_selection
= 127;
2168 uint8_t highest_note_in_selection
= 0;
2169 uint8_t highest_note_difference
= 0;
2171 // find highest and lowest notes first
2173 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2174 uint8_t pitch
= (*i
)->note()->note();
2175 lowest_note_in_selection
= std::min(lowest_note_in_selection
, pitch
);
2176 highest_note_in_selection
= std::max(highest_note_in_selection
, pitch
);
2180 cerr << "dnote: " << (int) dnote << endl;
2181 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2182 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2183 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2184 << int(highest_note_in_selection) << endl;
2185 cerr << "selection size: " << _selection.size() << endl;
2186 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2189 // Make sure the note pitch does not exceed the MIDI standard range
2190 if (highest_note_in_selection
+ dnote
> 127) {
2191 highest_note_difference
= highest_note_in_selection
- 127;
2194 start_note_diff_command (_("move notes"));
2196 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end() ; ++i
) {
2198 Evoral::MusicalTime new_time
= frames_to_beats (beats_to_frames ((*i
)->note()->time()) + dt
);
2204 note_diff_add_change (*i
, MidiModel::NoteDiffCommand::StartTime
, new_time
);
2206 uint8_t original_pitch
= (*i
)->note()->note();
2207 uint8_t new_pitch
= original_pitch
+ dnote
- highest_note_difference
;
2209 // keep notes in standard midi range
2210 clamp_to_0_127(new_pitch
);
2212 // keep original pitch if note is dragged outside valid midi range
2213 if ((original_pitch
!= 0 && new_pitch
== 0)
2214 || (original_pitch
!= 127 && new_pitch
== 127)) {
2215 new_pitch
= original_pitch
;
2218 lowest_note_in_selection
= std::min(lowest_note_in_selection
, new_pitch
);
2219 highest_note_in_selection
= std::max(highest_note_in_selection
, new_pitch
);
2221 note_diff_add_change (*i
, MidiModel::NoteDiffCommand::NoteNumber
, new_pitch
);
2226 // care about notes being moved beyond the upper/lower bounds on the canvas
2227 if (lowest_note_in_selection
< midi_stream_view()->lowest_note() ||
2228 highest_note_in_selection
> midi_stream_view()->highest_note()) {
2229 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange
);
2234 MidiRegionView::snap_pixel_to_frame(double x
)
2236 PublicEditor
& editor
= trackview
.editor();
2237 // x is region relative, convert it to global absolute frames
2238 framepos_t frame
= editor
.pixel_to_frame(x
) + _region
->position();
2239 editor
.snap_to(frame
);
2240 return frame
- _region
->position(); // convert back to region relative
2244 MidiRegionView::snap_frame_to_frame(framepos_t x
)
2246 PublicEditor
& editor
= trackview
.editor();
2247 // x is region relative, convert it to global absolute frames
2248 framepos_t frame
= x
+ _region
->position();
2249 editor
.snap_to(frame
);
2250 return frame
- _region
->position(); // convert back to region relative
2254 MidiRegionView::snap_to_pixel(double x
)
2256 return (double) trackview
.editor().frame_to_pixel(snap_pixel_to_frame(x
));
2260 MidiRegionView::get_position_pixels()
2262 framepos_t region_frame
= get_position();
2263 return trackview
.editor().frame_to_pixel(region_frame
);
2267 MidiRegionView::get_end_position_pixels()
2269 framepos_t frame
= get_position() + get_duration ();
2270 return trackview
.editor().frame_to_pixel(frame
);
2274 MidiRegionView::beats_to_frames(double beats
) const
2276 return _time_converter
.to(beats
);
2280 MidiRegionView::frames_to_beats(framepos_t frames
) const
2282 return _time_converter
.from(frames
);
2286 MidiRegionView::begin_resizing (bool /*at_front*/)
2288 _resize_data
.clear();
2290 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2291 CanvasNote
*note
= dynamic_cast<CanvasNote
*> (*i
);
2293 // only insert CanvasNotes into the map
2295 NoteResizeData
*resize_data
= new NoteResizeData();
2296 resize_data
->canvas_note
= note
;
2298 // create a new SimpleRect from the note which will be the resize preview
2299 SimpleRect
*resize_rect
= new SimpleRect(
2300 *_note_group
, note
->x1(), note
->y1(), note
->x2(), note
->y2());
2302 // calculate the colors: get the color settings
2303 uint32_t fill_color
= UINT_RGBA_CHANGE_A(
2304 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get(),
2307 // make the resize preview notes more transparent and bright
2308 fill_color
= UINT_INTERPOLATE(fill_color
, 0xFFFFFF40, 0.5);
2310 // calculate color based on note velocity
2311 resize_rect
->property_fill_color_rgba() = UINT_INTERPOLATE(
2312 CanvasNoteEvent::meter_style_fill_color(note
->note()->velocity(), note
->selected()),
2316 resize_rect
->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2317 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get());
2319 resize_data
->resize_rect
= resize_rect
;
2320 _resize_data
.push_back(resize_data
);
2325 /** Update resizing notes while user drags.
2326 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2327 * @param at_front which end of the note (true == note on, false == note off)
2328 * @param delta_x change in mouse position since the start of the drag
2329 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2330 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2331 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2332 * as the \a primary note.
2335 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent
* primary
, bool at_front
, double delta_x
, bool relative
)
2337 bool cursor_set
= false;
2339 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2340 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2341 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2346 current_x
= canvas_note
->x1() + delta_x
;
2348 current_x
= primary
->x1() + delta_x
;
2352 current_x
= canvas_note
->x2() + delta_x
;
2354 current_x
= primary
->x2() + delta_x
;
2359 resize_rect
->property_x1() = snap_to_pixel(current_x
);
2360 resize_rect
->property_x2() = canvas_note
->x2();
2362 resize_rect
->property_x2() = snap_to_pixel(current_x
);
2363 resize_rect
->property_x1() = canvas_note
->x1();
2369 beats
= snap_pixel_to_frame (current_x
);
2370 beats
= frames_to_beats (beats
);
2375 if (beats
< canvas_note
->note()->end_time()) {
2376 len
= canvas_note
->note()->time() - beats
;
2377 len
+= canvas_note
->note()->length();
2382 if (beats
>= canvas_note
->note()->time()) {
2383 len
= beats
- canvas_note
->note()->time();
2390 snprintf (buf
, sizeof (buf
), "%.3g beats", len
);
2391 trackview
.editor().show_verbose_canvas_cursor_with (buf
);
2400 /** Finish resizing notes when the user releases the mouse button.
2401 * Parameters the same as for \a update_resizing().
2404 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent
* primary
, bool at_front
, double delta_x
, bool relative
)
2406 start_note_diff_command (_("resize notes"));
2408 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2409 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2410 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2415 current_x
= canvas_note
->x1() + delta_x
;
2417 current_x
= primary
->x1() + delta_x
;
2421 current_x
= canvas_note
->x2() + delta_x
;
2423 current_x
= primary
->x2() + delta_x
;
2427 current_x
= snap_pixel_to_frame (current_x
);
2428 current_x
= frames_to_beats (current_x
);
2430 if (at_front
&& current_x
< canvas_note
->note()->end_time()) {
2431 note_diff_add_change (canvas_note
, MidiModel::NoteDiffCommand::StartTime
, current_x
);
2433 double len
= canvas_note
->note()->time() - current_x
;
2434 len
+= canvas_note
->note()->length();
2437 /* XXX convert to beats */
2438 note_diff_add_change (canvas_note
, MidiModel::NoteDiffCommand::Length
, len
);
2443 double len
= current_x
- canvas_note
->note()->time();
2446 /* XXX convert to beats */
2447 note_diff_add_change (canvas_note
, MidiModel::NoteDiffCommand::Length
, len
);
2455 _resize_data
.clear();
2460 MidiRegionView::change_note_channel (CanvasNoteEvent
* event
, int8_t channel
)
2462 note_diff_add_change (event
, MidiModel::NoteDiffCommand::Channel
, (uint8_t) channel
);
2466 MidiRegionView::change_note_velocity(CanvasNoteEvent
* event
, int8_t velocity
, bool relative
)
2468 uint8_t new_velocity
;
2471 new_velocity
= event
->note()->velocity() + velocity
;
2472 clamp_to_0_127(new_velocity
);
2474 new_velocity
= velocity
;
2477 event
->set_selected (event
->selected()); // change color
2479 note_diff_add_change (event
, MidiModel::NoteDiffCommand::Velocity
, new_velocity
);
2483 MidiRegionView::change_note_note (CanvasNoteEvent
* event
, int8_t note
, bool relative
)
2488 new_note
= event
->note()->note() + note
;
2493 clamp_to_0_127 (new_note
);
2494 note_diff_add_change (event
, MidiModel::NoteDiffCommand::NoteNumber
, new_note
);
2498 MidiRegionView::trim_note (CanvasNoteEvent
* event
, Evoral::MusicalTime front_delta
, Evoral::MusicalTime end_delta
)
2500 bool change_start
= false;
2501 bool change_length
= false;
2502 Evoral::MusicalTime new_start
= 0;
2503 Evoral::MusicalTime new_length
= 0;
2505 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2507 front_delta: if positive - move the start of the note later in time (shortening it)
2508 if negative - move the start of the note earlier in time (lengthening it)
2510 end_delta: if positive - move the end of the note later in time (lengthening it)
2511 if negative - move the end of the note earlier in time (shortening it)
2515 if (front_delta
< 0) {
2517 if (event
->note()->time() < -front_delta
) {
2520 new_start
= event
->note()->time() + front_delta
; // moves earlier
2523 /* start moved toward zero, so move the end point out to where it used to be.
2524 Note that front_delta is negative, so this increases the length.
2527 new_length
= event
->note()->length() - front_delta
;
2528 change_start
= true;
2529 change_length
= true;
2533 Evoral::MusicalTime new_pos
= event
->note()->time() + front_delta
;
2535 if (new_pos
< event
->note()->end_time()) {
2536 new_start
= event
->note()->time() + front_delta
;
2537 /* start moved toward the end, so move the end point back to where it used to be */
2538 new_length
= event
->note()->length() - front_delta
;
2539 change_start
= true;
2540 change_length
= true;
2547 bool can_change
= true;
2548 if (end_delta
< 0) {
2549 if (event
->note()->length() < -end_delta
) {
2555 new_length
= event
->note()->length() + end_delta
;
2556 change_length
= true;
2561 note_diff_add_change (event
, MidiModel::NoteDiffCommand::StartTime
, new_start
);
2564 if (change_length
) {
2565 note_diff_add_change (event
, MidiModel::NoteDiffCommand::Length
, new_length
);
2570 MidiRegionView::change_note_time (CanvasNoteEvent
* event
, Evoral::MusicalTime delta
, bool relative
)
2572 Evoral::MusicalTime new_time
;
2576 if (event
->note()->time() < -delta
) {
2579 new_time
= event
->note()->time() + delta
;
2582 new_time
= event
->note()->time() + delta
;
2588 note_diff_add_change (event
, MidiModel::NoteDiffCommand::StartTime
, new_time
);
2592 MidiRegionView::change_note_length (CanvasNoteEvent
* event
, Evoral::MusicalTime t
)
2594 note_diff_add_change (event
, MidiModel::NoteDiffCommand::Length
, t
);
2598 MidiRegionView::change_velocities (bool up
, bool fine
, bool allow_smush
)
2602 if (_selection
.empty()) {
2617 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2618 if ((*i
)->note()->velocity() + delta
== 0 || (*i
)->note()->velocity() + delta
== 127) {
2624 start_note_diff_command (_("change velocities"));
2626 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end();) {
2627 Selection::iterator next
= i
;
2629 change_note_velocity (*i
, delta
, true);
2635 if (!_selection
.empty()) {
2637 snprintf (buf
, sizeof (buf
), "Vel %d",
2638 (int) (*_selection
.begin())->note()->velocity());
2639 trackview
.editor().show_verbose_canvas_cursor_with (buf
, 10, 10);
2645 MidiRegionView::transpose (bool up
, bool fine
, bool allow_smush
)
2647 if (_selection
.empty()) {
2664 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2666 if ((int8_t) (*i
)->note()->note() + delta
<= 0) {
2670 if ((int8_t) (*i
)->note()->note() + delta
> 127) {
2677 start_note_diff_command (_("transpose"));
2679 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2680 Selection::iterator next
= i
;
2682 change_note_note (*i
, delta
, true);
2690 MidiRegionView::change_note_lengths (bool fine
, bool shorter
, Evoral::MusicalTime delta
, bool start
, bool end
)
2696 /* grab the current grid distance */
2698 delta
= trackview
.editor().get_grid_type_as_beats (success
, _region
->position());
2700 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2701 cerr
<< "Grid type not available as beats - TO BE FIXED\n";
2711 start_note_diff_command (_("change note lengths"));
2713 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2714 Selection::iterator next
= i
;
2717 /* note the negation of the delta for start */
2719 trim_note (*i
, (start
? -delta
: 0), (end
? delta
: 0));
2728 MidiRegionView::nudge_notes (bool forward
)
2730 if (_selection
.empty()) {
2734 /* pick a note as the point along the timeline to get the nudge distance.
2735 its not necessarily the earliest note, so we may want to pull the notes out
2736 into a vector and sort before using the first one.
2739 framepos_t ref_point
= _region
->position() + beats_to_frames ((*(_selection
.begin()))->note()->time());
2741 framepos_t distance
;
2743 if (trackview
.editor().snap_mode() == Editing::SnapOff
) {
2745 /* grid is off - use nudge distance */
2747 distance
= trackview
.editor().get_nudge_distance (ref_point
, unused
);
2753 framepos_t next_pos
= ref_point
;
2756 if (max_framepos
- 1 < next_pos
) {
2760 if (next_pos
== 0) {
2766 trackview
.editor().snap_to (next_pos
, (forward
? 1 : -1), false);
2767 distance
= ref_point
- next_pos
;
2770 if (distance
== 0) {
2774 Evoral::MusicalTime delta
= frames_to_beats (fabs (distance
));
2780 start_note_diff_command (_("nudge"));
2782 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2783 Selection::iterator next
= i
;
2785 change_note_time (*i
, delta
, true);
2793 MidiRegionView::change_channel(uint8_t channel
)
2795 start_note_diff_command(_("change channel"));
2796 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2797 note_diff_add_change (*i
, MidiModel::NoteDiffCommand::Channel
, channel
);
2805 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent
* ev
)
2807 Editor
* editor
= dynamic_cast<Editor
*>(&trackview
.editor());
2809 pre_enter_cursor
= editor
->get_canvas_cursor ();
2811 if (_mouse_state
== SelectTouchDragging
) {
2812 note_selected (ev
, true);
2815 show_verbose_canvas_cursor (ev
->note ());
2819 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent
*)
2821 Editor
* editor
= dynamic_cast<Editor
*>(&trackview
.editor());
2823 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2824 (*i
)->hide_velocity ();
2827 editor
->hide_verbose_canvas_cursor ();
2829 if (pre_enter_cursor
) {
2830 editor
->set_canvas_cursor (pre_enter_cursor
);
2831 pre_enter_cursor
= 0;
2836 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange
* ev
)
2839 s
<< ((int) ev
->patch()->program() + 1) << ":" << (ev
->patch()->bank() + 1);
2840 trackview
.editor().show_verbose_canvas_cursor_with (s
.str().c_str(), 10, 20);
2844 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange
*)
2846 trackview
.editor().hide_verbose_canvas_cursor ();
2850 MidiRegionView::note_mouse_position (float x_fraction
, float /*y_fraction*/, bool can_set_cursor
)
2852 Editor
* editor
= dynamic_cast<Editor
*>(&trackview
.editor());
2854 if (x_fraction
> 0.0 && x_fraction
< 0.25) {
2855 editor
->set_canvas_cursor (editor
->cursors()->left_side_trim
);
2856 } else if (x_fraction
>= 0.75 && x_fraction
< 1.0) {
2857 editor
->set_canvas_cursor (editor
->cursors()->right_side_trim
);
2859 if (pre_enter_cursor
&& can_set_cursor
) {
2860 editor
->set_canvas_cursor (pre_enter_cursor
);
2866 MidiRegionView::set_frame_color()
2873 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase
.get();
2874 } else if (high_enough_for_name
) {
2875 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase
.get();
2877 frame
->property_fill_color_rgba() = fill_color
;
2882 MidiRegionView::midi_channel_mode_changed(ChannelMode mode
, uint16_t mask
)
2886 case FilterChannels
:
2887 _force_channel
= -1;
2890 _force_channel
= mask
;
2891 mask
= 0xFFFF; // Show all notes as active (below)
2894 // Update notes for selection
2895 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2896 (*i
)->on_channel_selection_change(mask
);
2899 _last_channel_selection
= mask
;
2901 _patch_changes
.clear ();
2902 display_patch_changes ();
2906 MidiRegionView::midi_patch_settings_changed(std::string model
, std::string custom_device_mode
)
2908 _model_name
= model
;
2909 _custom_device_mode
= custom_device_mode
;
2914 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op
)
2916 if (_selection
.empty()) {
2920 PublicEditor
& editor (trackview
.editor());
2925 editor
.get_cut_buffer().add (selection_as_cut_buffer());
2933 start_note_diff_command();
2935 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2941 note_diff_remove_note (*i
);
2951 MidiRegionView::selection_as_cut_buffer () const
2955 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2956 NoteType
* n
= (*i
)->note().get();
2957 notes
.insert (boost::shared_ptr
<NoteType
> (new NoteType (*n
)));
2960 MidiCutBuffer
* cb
= new MidiCutBuffer (trackview
.session());
2967 MidiRegionView::paste (framepos_t pos
, float times
, const MidiCutBuffer
& mcb
)
2973 DEBUG_TRACE (DEBUG::CutNPaste
, string_compose ("MIDI paste @ %1 times %2\n", pos
, times
));
2975 start_note_diff_command (_("paste"));
2977 Evoral::MusicalTime beat_delta
;
2978 Evoral::MusicalTime paste_pos_beats
;
2979 Evoral::MusicalTime duration
;
2980 Evoral::MusicalTime end_point
= 0;
2982 duration
= (*mcb
.notes().rbegin())->end_time() - (*mcb
.notes().begin())->time();
2983 paste_pos_beats
= frames_to_beats (pos
- _region
->position());
2984 beat_delta
= (*mcb
.notes().begin())->time() - paste_pos_beats
;
2985 paste_pos_beats
= 0;
2987 DEBUG_TRACE (DEBUG::CutNPaste
, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6 ; beat delta = %7\n",
2988 (*mcb
.notes().begin())->time(),
2989 (*mcb
.notes().rbegin())->end_time(),
2990 duration
, pos
, _region
->position(),
2991 paste_pos_beats
, beat_delta
));
2995 for (int n
= 0; n
< (int) times
; ++n
) {
2997 for (Notes::const_iterator i
= mcb
.notes().begin(); i
!= mcb
.notes().end(); ++i
) {
2999 boost::shared_ptr
<NoteType
> copied_note (new NoteType (*((*i
).get())));
3000 copied_note
->set_time (paste_pos_beats
+ copied_note
->time() - beat_delta
);
3002 /* make all newly added notes selected */
3004 note_diff_add_note (copied_note
, true);
3005 end_point
= copied_note
->end_time();
3008 paste_pos_beats
+= duration
;
3011 /* if we pasted past the current end of the region, extend the region */
3013 framepos_t end_frame
= _region
->position() + beats_to_frames (end_point
);
3014 framepos_t region_end
= _region
->position() + _region
->length() - 1;
3016 if (end_frame
> region_end
) {
3018 DEBUG_TRACE (DEBUG::CutNPaste
, string_compose ("Paste extended region from %1 to %2\n", region_end
, end_frame
));
3020 trackview
.session()->begin_reversible_command (_("paste"));
3022 _region
->clear_changes ();
3023 _region
->set_length (end_frame
, this);
3024 trackview
.session()->add_command (new StatefulDiffCommand (_region
));
3030 struct EventNoteTimeEarlyFirstComparator
{
3031 bool operator() (CanvasNoteEvent
* a
, CanvasNoteEvent
* b
) {
3032 return a
->note()->time() < b
->note()->time();
3037 MidiRegionView::time_sort_events ()
3039 if (!_sort_needed
) {
3043 EventNoteTimeEarlyFirstComparator cmp
;
3046 _sort_needed
= false;
3050 MidiRegionView::goto_next_note ()
3052 // framepos_t pos = -1;
3053 bool use_next
= false;
3055 if (_events
.back()->selected()) {
3059 time_sort_events ();
3061 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
3062 if ((*i
)->selected()) {
3065 } else if (use_next
) {
3067 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3072 /* use the first one */
3074 unique_select (_events
.front());
3079 MidiRegionView::goto_previous_note ()
3081 // framepos_t pos = -1;
3082 bool use_next
= false;
3084 if (_events
.front()->selected()) {
3088 time_sort_events ();
3090 for (Events::reverse_iterator i
= _events
.rbegin(); i
!= _events
.rend(); ++i
) {
3091 if ((*i
)->selected()) {
3094 } else if (use_next
) {
3096 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3101 /* use the last one */
3103 unique_select (*(_events
.rbegin()));
3107 MidiRegionView::selection_as_notelist (Notes
& selected
, bool allow_all_if_none_selected
)
3109 bool had_selected
= false;
3111 time_sort_events ();
3113 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
3114 if ((*i
)->selected()) {
3115 selected
.insert ((*i
)->note());
3116 had_selected
= true;
3120 if (allow_all_if_none_selected
&& !had_selected
) {
3121 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
3122 selected
.insert ((*i
)->note());
3128 MidiRegionView::update_ghost_note (double x
, double y
)
3133 _note_group
->w2i (x
, y
);
3134 framepos_t f
= trackview
.editor().pixel_to_frame (x
) + _region
->position ();
3135 trackview
.editor().snap_to (f
);
3136 f
-= _region
->position ();
3139 Evoral::MusicalTime beats
= trackview
.editor().get_grid_type_as_beats (success
, f
);
3145 double length
= frames_to_beats (snap_frame_to_frame (f
+ beats_to_frames (beats
)) - f
);
3147 _ghost_note
->note()->set_time (frames_to_beats (f
+ _region
->start()));
3148 _ghost_note
->note()->set_length (length
);
3149 _ghost_note
->note()->set_note (midi_stream_view()->y_to_note (y
));
3151 update_note (_ghost_note
);
3153 show_verbose_canvas_cursor (_ghost_note
->note ());
3157 MidiRegionView::create_ghost_note (double x
, double y
)
3162 boost::shared_ptr
<NoteType
> g (new NoteType
);
3163 _ghost_note
= new NoEventCanvasNote (*this, *_note_group
, g
);
3164 update_ghost_note (x
, y
);
3165 _ghost_note
->show ();
3170 show_verbose_canvas_cursor (_ghost_note
->note ());
3174 MidiRegionView::snap_changed ()
3180 create_ghost_note (_last_ghost_x
, _last_ghost_y
);
3184 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr
<NoteType
> n
) const
3187 snprintf (buf
, sizeof (buf
), "%s (%d)\nVel %d",
3188 Evoral::midi_note_name (n
->note()).c_str(),
3190 (int) n
->velocity());
3191 trackview
.editor().show_verbose_canvas_cursor_with (buf
, 10, 20);
3195 MidiRegionView::drop_down_keys ()
3197 _mouse_state
= None
;
3201 MidiRegionView::maybe_select_by_position (GdkEventButton
* ev
, double /*x*/, double y
)
3203 double note
= midi_stream_view()->y_to_note(y
);
3205 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
3207 uint16_t chn_mask
= mtv
->channel_selector().get_selected_channels();
3209 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::TertiaryModifier
)) {
3210 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchGreaterThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
3211 } else if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
3212 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchLessThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
3217 bool add_mrv_selection
= false;
3219 if (_selection
.empty()) {
3220 add_mrv_selection
= true;
3223 for (Events::iterator i
= e
.begin(); i
!= e
.end(); ++i
) {
3224 if (_selection
.insert (*i
).second
) {
3225 (*i
)->set_selected (true);
3229 if (add_mrv_selection
) {
3230 PublicEditor
& editor (trackview
.editor());
3231 editor
.get_selection().add (this);
3236 MidiRegionView::color_handler ()
3238 RegionView::color_handler ();
3240 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
3241 (*i
)->set_selected ((*i
)->selected()); // will change color
3244 /* XXX probably more to do here */
3248 MidiRegionView::enable_display (bool yn
)
3250 RegionView::enable_display (yn
);
3257 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos
)
3259 if (_step_edit_cursor
== 0) {
3260 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
3262 _step_edit_cursor
= new ArdourCanvas::SimpleRect (*group
);
3263 _step_edit_cursor
->property_y1() = 0;
3264 _step_edit_cursor
->property_y2() = midi_stream_view()->contents_height();
3265 _step_edit_cursor
->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3266 _step_edit_cursor
->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3269 move_step_edit_cursor (pos
);
3270 _step_edit_cursor
->show ();
3274 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos
)
3276 _step_edit_cursor_position
= pos
;
3278 if (_step_edit_cursor
) {
3279 double pixel
= trackview
.editor().frame_to_pixel (beats_to_frames (pos
));
3280 _step_edit_cursor
->property_x1() = pixel
;
3281 set_step_edit_cursor_width (_step_edit_cursor_width
);
3286 MidiRegionView::hide_step_edit_cursor ()
3288 if (_step_edit_cursor
) {
3289 _step_edit_cursor
->hide ();
3294 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats
)
3296 _step_edit_cursor_width
= beats
;
3298 if (_step_edit_cursor
) {
3299 _step_edit_cursor
->property_x2() = _step_edit_cursor
->property_x1() + trackview
.editor().frame_to_pixel (beats_to_frames (beats
));
3303 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3304 * @param buf Data that has been recorded.
3305 * @param w Source that this data will end up in.
3308 MidiRegionView::data_recorded (boost::shared_ptr
<MidiBuffer
> buf
, boost::weak_ptr
<MidiSource
> w
)
3310 if (!_active_notes
) {
3311 /* we aren't actively being recorded to */
3315 boost::shared_ptr
<MidiSource
> src
= w
.lock ();
3316 if (!src
|| src
!= midi_region()->midi_source()) {
3317 /* recorded data was not destined for our source */
3321 MidiTimeAxisView
* mtv
= dynamic_cast<MidiTimeAxisView
*> (&trackview
);
3322 BeatsFramesConverter
converter (trackview
.session()->tempo_map(), mtv
->midi_track()->get_capture_start_frame (0));
3324 framepos_t back
= max_framepos
;
3326 for (MidiBuffer::iterator i
= buf
->begin(); i
!= buf
->end(); ++i
) {
3327 Evoral::MIDIEvent
<MidiBuffer::TimeType
> const ev (*i
, false);
3328 assert (ev
.buffer ());
3330 Evoral::MusicalTime
const time_beats
= converter
.from (ev
.time () - converter
.origin_b ());
3332 if (ev
.type() == MIDI_CMD_NOTE_ON
) {
3334 boost::shared_ptr
<Evoral::Note
<Evoral::MusicalTime
> > note (
3335 new Evoral::Note
<Evoral::MusicalTime
> (ev
.channel(), time_beats
, 0, ev
.note(), ev
.velocity())
3338 add_note (note
, true);
3340 /* fix up our note range */
3341 if (ev
.note() < _current_range_min
) {
3342 midi_stream_view()->apply_note_range (ev
.note(), _current_range_max
, true);
3343 } else if (ev
.note() > _current_range_max
) {
3344 midi_stream_view()->apply_note_range (_current_range_min
, ev
.note(), true);
3347 } else if (ev
.type() == MIDI_CMD_NOTE_OFF
) {
3348 resolve_note (ev
.note (), time_beats
);
3354 midi_stream_view()->check_record_layers (region(), back
);
3358 MidiRegionView::trim_front_starting ()
3360 /* Reparent the note group to the region view's parent, so that it doesn't change
3361 when the region view is trimmed.
3363 _temporary_note_group
= new ArdourCanvas::Group (*group
->property_parent ());
3364 _temporary_note_group
->move (group
->property_x(), group
->property_y());
3365 _note_group
->reparent (*_temporary_note_group
);
3369 MidiRegionView::trim_front_ending ()
3371 _note_group
->reparent (*group
);
3372 delete _temporary_note_group
;
3373 _temporary_note_group
= 0;
3375 if (_region
->start() < 0) {
3376 /* Trim drag made start time -ve; fix this */
3377 midi_region()->fix_negative_start ();
3381 /** @return channel (counted from 0) to add an event to, based on the current setting
3382 * of the channel selector.
3385 MidiRegionView::get_channel_for_add () const
3387 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
3388 uint16_t const chn_mask
= mtv
->channel_selector().get_selected_channels();
3390 uint8_t channel
= 0;
3392 /* pick the highest selected channel, unless all channels are selected,
3393 which is interpreted to mean channel 1 (zero)
3396 for (uint16_t i
= 0; i
< 16; ++i
) {
3397 if (chn_mask
& (1<<i
)) {
3403 if (chn_cnt
== 16) {
3411 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange
* pc
)
3413 PatchChangeDialog
d (&_time_converter
, trackview
.session(), *pc
->patch (), Gtk::Stock::APPLY
);
3414 if (d
.run () != Gtk::RESPONSE_ACCEPT
) {
3418 change_patch_change (pc
->patch(), d
.patch ());