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"
53 #include "ghostregion.h"
54 #include "gui_thread.h"
56 #include "midi_cut_buffer.h"
57 #include "midi_list_editor.h"
58 #include "midi_region_view.h"
59 #include "midi_streamview.h"
60 #include "midi_time_axis.h"
61 #include "midi_util.h"
62 #include "note_player.h"
63 #include "public_editor.h"
64 #include "rgb_macros.h"
65 #include "selection.h"
66 #include "simpleline.h"
67 #include "streamview.h"
69 #include "mouse_cursors.h"
70 #include "patch_change_dialog.h"
74 using namespace ARDOUR
;
76 using namespace Editing
;
77 using namespace ArdourCanvas
;
78 using Gtkmm2ext::Keyboard
;
80 MidiRegionView::MidiRegionView (ArdourCanvas::Group
*parent
, RouteTimeAxisView
&tv
,
81 boost::shared_ptr
<MidiRegion
> r
, double spu
, Gdk::Color
const & basic_color
)
82 : RegionView (parent
, tv
, r
, spu
, basic_color
)
84 , _last_channel_selection(0xFFFF)
85 , _current_range_min(0)
86 , _current_range_max(0)
87 , _model_name(string())
88 , _custom_device_mode(string())
90 , _note_group(new ArdourCanvas::Group(*group
))
91 , _note_diff_command (0)
94 , _step_edit_cursor (0)
95 , _step_edit_cursor_width (1.0)
96 , _step_edit_cursor_position (0.0)
97 , _channel_selection_scoped_note (0)
98 , _temporary_note_group (0)
101 , _sort_needed (true)
102 , _optimization_iterator (_events
.end())
104 , no_sound_notes (false)
107 , pre_enter_cursor (0)
109 _note_group
->raise_to_top();
110 PublicEditor::DropDownKeys
.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys
));
112 connect_to_diskstream ();
115 MidiRegionView::MidiRegionView (ArdourCanvas::Group
*parent
, RouteTimeAxisView
&tv
,
116 boost::shared_ptr
<MidiRegion
> r
, double spu
, Gdk::Color
& basic_color
,
117 TimeAxisViewItem::Visibility visibility
)
118 : RegionView (parent
, tv
, r
, spu
, basic_color
, false, visibility
)
120 , _last_channel_selection(0xFFFF)
121 , _model_name(string())
122 , _custom_device_mode(string())
124 , _note_group(new ArdourCanvas::Group(*parent
))
125 , _note_diff_command (0)
128 , _step_edit_cursor (0)
129 , _step_edit_cursor_width (1.0)
130 , _step_edit_cursor_position (0.0)
131 , _channel_selection_scoped_note (0)
132 , _temporary_note_group (0)
135 , _sort_needed (true)
136 , _optimization_iterator (_events
.end())
138 , no_sound_notes (false)
142 _note_group
->raise_to_top();
143 PublicEditor::DropDownKeys
.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys
));
145 connect_to_diskstream ();
148 MidiRegionView::MidiRegionView (const MidiRegionView
& other
)
149 : sigc::trackable(other
)
152 , _last_channel_selection(0xFFFF)
153 , _model_name(string())
154 , _custom_device_mode(string())
156 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
157 , _note_diff_command (0)
160 , _step_edit_cursor (0)
161 , _step_edit_cursor_width (1.0)
162 , _step_edit_cursor_position (0.0)
163 , _channel_selection_scoped_note (0)
164 , _temporary_note_group (0)
167 , _sort_needed (true)
168 , _optimization_iterator (_events
.end())
170 , no_sound_notes (false)
177 UINT_TO_RGBA (other
.fill_color
, &r
, &g
, &b
, &a
);
178 c
.set_rgb_p (r
/255.0, g
/255.0, b
/255.0);
183 MidiRegionView::MidiRegionView (const MidiRegionView
& other
, boost::shared_ptr
<MidiRegion
> region
)
184 : RegionView (other
, boost::shared_ptr
<Region
> (region
))
186 , _last_channel_selection(0xFFFF)
187 , _model_name(string())
188 , _custom_device_mode(string())
190 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
191 , _note_diff_command (0)
194 , _step_edit_cursor (0)
195 , _step_edit_cursor_width (1.0)
196 , _step_edit_cursor_position (0.0)
197 , _channel_selection_scoped_note (0)
198 , _temporary_note_group (0)
201 , _sort_needed (true)
202 , _optimization_iterator (_events
.end())
204 , no_sound_notes (false)
211 UINT_TO_RGBA (other
.fill_color
, &r
, &g
, &b
, &a
);
212 c
.set_rgb_p (r
/255.0, g
/255.0, b
/255.0);
218 MidiRegionView::init (Gdk::Color
const & basic_color
, bool wfd
)
220 PublicEditor::DropDownKeys
.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys
));
222 CanvasNoteEvent::CanvasNoteEventDeleted
.connect (note_delete_connection
, MISSING_INVALIDATOR
,
223 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection
, this, _1
),
227 midi_region()->midi_source(0)->load_model();
230 _model
= midi_region()->midi_source(0)->model();
231 _enable_display
= false;
233 RegionView::init (basic_color
, false);
235 compute_colors (basic_color
);
237 set_height (trackview
.current_height());
240 region_sync_changed ();
241 region_resized (ARDOUR::bounds_change
);
244 reset_width_dependent_items (_pixel_width
);
248 _enable_display
= true;
251 display_model (_model
);
255 group
->raise_to_top();
256 group
->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event
), false);
258 midi_view()->signal_channel_mode_changed().connect(
259 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed
));
261 midi_view()->signal_midi_patch_settings_changed().connect(
262 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed
));
264 trackview
.editor().SnapChanged
.connect (snap_changed_connection
, invalidator (*this), ui_bind (&MidiRegionView::snap_changed
, this), gui_context ());
266 connect_to_diskstream ();
270 MidiRegionView::connect_to_diskstream ()
272 midi_view()->midi_track()->DataRecorded
.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::data_recorded
, this, _1
, _2
), gui_context ());
276 MidiRegionView::canvas_event(GdkEvent
* ev
)
279 case GDK_ENTER_NOTIFY
:
280 case GDK_LEAVE_NOTIFY
:
281 _last_event_x
= ev
->crossing
.x
;
282 _last_event_y
= ev
->crossing
.y
;
284 case GDK_MOTION_NOTIFY
:
285 _last_event_x
= ev
->motion
.x
;
286 _last_event_y
= ev
->motion
.y
;
292 if (!trackview
.editor().internal_editing()) {
296 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
297 to its items, which means that ev->type == GDK_SCROLL will never be seen
302 return scroll (&ev
->scroll
);
305 return key_press (&ev
->key
);
307 case GDK_KEY_RELEASE
:
308 return key_release (&ev
->key
);
310 case GDK_BUTTON_PRESS
:
311 return button_press (&ev
->button
);
313 case GDK_2BUTTON_PRESS
:
316 case GDK_BUTTON_RELEASE
:
317 return button_release (&ev
->button
);
319 case GDK_ENTER_NOTIFY
:
320 return enter_notify (&ev
->crossing
);
322 case GDK_LEAVE_NOTIFY
:
323 return leave_notify (&ev
->crossing
);
325 case GDK_MOTION_NOTIFY
:
326 return motion (&ev
->motion
);
336 MidiRegionView::remove_ghost_note ()
343 MidiRegionView::enter_notify (GdkEventCrossing
* ev
)
345 trackview
.editor().MouseModeChanged
.connect (
346 _mouse_mode_connection
, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed
, this), gui_context ()
349 Keyboard::magic_widget_grab_focus();
352 if (trackview
.editor().current_mouse_mode() == MouseRange
) {
353 create_ghost_note (ev
->x
, ev
->y
);
360 MidiRegionView::leave_notify (GdkEventCrossing
*)
362 _mouse_mode_connection
.disconnect ();
364 trackview
.editor().hide_verbose_canvas_cursor ();
365 remove_ghost_note ();
370 MidiRegionView::mouse_mode_changed ()
372 if (trackview
.editor().current_mouse_mode() == MouseRange
&& trackview
.editor().internal_editing()) {
373 create_ghost_note (_last_event_x
, _last_event_y
);
375 remove_ghost_note ();
376 trackview
.editor().hide_verbose_canvas_cursor ();
381 MidiRegionView::button_press (GdkEventButton
* ev
)
386 group
->w2i (_last_x
, _last_y
);
388 if (_mouse_state
!= SelectTouchDragging
) {
390 _pressed_button
= ev
->button
;
391 _mouse_state
= Pressed
;
396 _pressed_button
= ev
->button
;
402 MidiRegionView::button_release (GdkEventButton
* ev
)
404 double event_x
, event_y
;
405 framepos_t event_frame
= 0;
410 group
->w2i(event_x
, event_y
);
411 group
->ungrab(ev
->time
);
413 event_frame
= trackview
.editor().pixel_to_frame(event_x
);
415 switch (_mouse_state
) {
416 case Pressed
: // Clicked
418 switch (trackview
.editor().current_mouse_mode()) {
424 if (Keyboard::is_insert_note_event(ev
)){
426 double event_x
, event_y
;
430 group
->w2i(event_x
, event_y
);
433 Evoral::MusicalTime beats
= trackview
.editor().get_grid_type_as_beats (success
, trackview
.editor().pixel_to_frame (event_x
));
439 create_note_at (event_x
, event_y
, beats
, true);
447 Evoral::MusicalTime beats
= trackview
.editor().get_grid_type_as_beats (success
, trackview
.editor().pixel_to_frame (event_x
));
453 create_note_at (event_x
, event_y
, beats
, true);
464 case SelectRectDragging
: // Select drag done
471 case AddDragging
: // Add drag done
475 if (Keyboard::is_insert_note_event(ev
) || trackview
.editor().current_mouse_mode() == MouseRange
){
477 if (_drag_rect
->property_x2() > _drag_rect
->property_x1() + 2) {
479 const double x
= _drag_rect
->property_x1();
480 const double length
= trackview
.editor().pixel_to_frame (_drag_rect
->property_x2() - _drag_rect
->property_x1());
482 create_note_at (x
, _drag_rect
->property_y1(), frames_to_beats(length
), true);
489 create_ghost_note (ev
->x
, ev
->y
);
499 MidiRegionView::motion (GdkEventMotion
* ev
)
501 double event_x
, event_y
;
502 framepos_t event_frame
= 0;
506 group
->w2i(event_x
, event_y
);
508 // convert event_x to global frame
509 event_frame
= trackview
.editor().pixel_to_frame(event_x
) + _region
->position();
510 trackview
.editor().snap_to(event_frame
);
512 // convert event_frame back to local coordinates relative to position
513 event_frame
-= _region
->position();
515 if (!_ghost_note
&& trackview
.editor().current_mouse_mode() != MouseRange
516 && Keyboard::modifier_state_contains (ev
->state
, Keyboard::insert_note_modifier())
517 && _mouse_state
!= AddDragging
){
519 create_ghost_note (ev
->x
, ev
->y
);
521 else if (_ghost_note
&& trackview
.editor().current_mouse_mode() != MouseRange
522 && Keyboard::modifier_state_contains (ev
->state
, Keyboard::insert_note_modifier())){
524 update_ghost_note (ev
->x
, ev
->y
);
526 else if (_ghost_note
&& trackview
.editor().current_mouse_mode() != MouseRange
){
531 trackview
.editor().hide_verbose_canvas_cursor ();
533 else if (_ghost_note
&& trackview
.editor().current_mouse_mode() == MouseRange
) {
534 update_ghost_note (ev
->x
, ev
->y
);
537 /* any motion immediately hides velocity text that may have been visible */
539 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
540 (*i
)->hide_velocity ();
543 switch (_mouse_state
) {
544 case Pressed
: // Maybe start a drag, if we've moved a bit
546 if (fabs (event_x
- _last_x
) < 1 && fabs (event_y
- _last_y
) < 1) {
547 /* no appreciable movement since the button was pressed */
552 if (_pressed_button
== 1 && trackview
.editor().current_mouse_mode() == MouseObject
553 && !Keyboard::modifier_state_contains (ev
->state
, Keyboard::insert_note_modifier())) {
555 group
->grab(GDK_POINTER_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
,
556 Gdk::Cursor(Gdk::FLEUR
), ev
->time
);
560 _drag_start_x
= event_x
;
561 _drag_start_y
= event_y
;
563 _drag_rect
= new ArdourCanvas::SimpleRect(*group
);
564 _drag_rect
->property_x1() = event_x
;
565 _drag_rect
->property_y1() = event_y
;
566 _drag_rect
->property_x2() = event_x
;
567 _drag_rect
->property_y2() = event_y
;
568 _drag_rect
->property_outline_what() = 0xFF;
569 _drag_rect
->property_outline_color_rgba()
570 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline
.get();
571 _drag_rect
->property_fill_color_rgba()
572 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill
.get();
574 _mouse_state
= SelectRectDragging
;
577 // Add note drag start
578 } else if (trackview
.editor().internal_editing()) {
583 group
->grab(GDK_POINTER_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
,
584 Gdk::Cursor(Gdk::FLEUR
), ev
->time
);
588 _drag_start_x
= event_x
;
589 _drag_start_y
= event_y
;
591 _drag_rect
= new ArdourCanvas::SimpleRect(*group
);
592 _drag_rect
->property_x1() = trackview
.editor().frame_to_pixel(event_frame
);
594 _drag_rect
->property_y1() = midi_stream_view()->note_to_y(
595 midi_stream_view()->y_to_note(event_y
));
596 _drag_rect
->property_x2() = trackview
.editor().frame_to_pixel(event_frame
);
597 _drag_rect
->property_y2() = _drag_rect
->property_y1()
598 + floor(midi_stream_view()->note_height());
599 _drag_rect
->property_outline_what() = 0xFF;
600 _drag_rect
->property_outline_color_rgba() = 0xFFFFFF99;
601 _drag_rect
->property_fill_color_rgba() = 0xFFFFFF66;
603 _mouse_state
= AddDragging
;
610 trackview
.editor().hide_verbose_canvas_cursor ();
618 case SelectRectDragging
: // Select drag motion
619 case AddDragging
: // Add note drag motion
624 GdkModifierType state
;
625 gdk_window_get_pointer(ev
->window
, &t_x
, &t_y
, &state
);
630 if (_mouse_state
== AddDragging
){
631 event_x
= trackview
.editor().frame_to_pixel(event_frame
);
636 if (event_x
> _drag_start_x
){
637 _drag_rect
->property_x2() = event_x
;
640 _drag_rect
->property_x1() = event_x
;
644 if (_drag_rect
&& _mouse_state
== SelectRectDragging
) {
646 if (event_y
> _drag_start_y
){
647 _drag_rect
->property_y2() = event_y
;
650 _drag_rect
->property_y1() = event_y
;
653 update_drag_selection(_drag_start_x
, event_x
, _drag_start_y
, event_y
);
659 case SelectTouchDragging
:
671 MidiRegionView::scroll (GdkEventScroll
* ev
)
673 if (_selection
.empty()) {
677 trackview
.editor().hide_verbose_canvas_cursor ();
679 bool fine
= !Keyboard::modifier_state_equals (ev
->state
, Keyboard::SecondaryModifier
);
681 if (ev
->direction
== GDK_SCROLL_UP
) {
682 change_velocities (true, fine
, false);
683 } else if (ev
->direction
== GDK_SCROLL_DOWN
) {
684 change_velocities (false, fine
, false);
690 MidiRegionView::key_press (GdkEventKey
* ev
)
692 /* since GTK bindings are generally activated on press, and since
693 detectable auto-repeat is the name of the game and only sends
694 repeated presses, carry out key actions at key press, not release.
697 if (ev
->keyval
== GDK_Alt_L
|| ev
->keyval
== GDK_Alt_R
){
698 _mouse_state
= SelectTouchDragging
;
701 } else if (ev
->keyval
== GDK_Escape
) {
705 } else if (ev
->keyval
== GDK_comma
|| ev
->keyval
== GDK_period
) {
707 bool start
= (ev
->keyval
== GDK_comma
);
708 bool end
= (ev
->keyval
== GDK_period
);
709 bool shorter
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
);
710 bool fine
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
712 change_note_lengths (fine
, shorter
, 0.0, start
, end
);
716 } else if (ev
->keyval
== GDK_Delete
) {
721 } else if (ev
->keyval
== GDK_Tab
) {
723 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
724 goto_previous_note ();
730 } else if (ev
->keyval
== GDK_Up
) {
732 bool allow_smush
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::TertiaryModifier
);
733 bool fine
= !Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
735 if (Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
)) {
736 change_velocities (true, fine
, allow_smush
);
738 transpose (true, fine
, allow_smush
);
742 } else if (ev
->keyval
== GDK_Down
) {
744 bool allow_smush
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::TertiaryModifier
);
745 bool fine
= !Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
747 if (Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
)) {
748 change_velocities (false, fine
, allow_smush
);
750 transpose (false, fine
, allow_smush
);
754 } else if (ev
->keyval
== GDK_Left
) {
759 } else if (ev
->keyval
== GDK_Right
) {
764 } else if (ev
->keyval
== GDK_Control_L
) {
773 MidiRegionView::key_release (GdkEventKey
* ev
)
775 if (ev
->keyval
== GDK_Alt_L
|| ev
->keyval
== GDK_Alt_R
) {
783 MidiRegionView::show_list_editor ()
786 _list_editor
= new MidiListEditor (trackview
.session(), midi_region());
788 _list_editor
->present ();
791 /** Add a note to the model, and the view, at a canvas (click) coordinate.
792 * \param x horizontal position in pixels
793 * \param y vertical position in pixels
794 * \param length duration of the note in beats, which will be snapped to the grid
795 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
798 MidiRegionView::create_note_at(double x
, double y
, double length
, bool sh
)
800 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
801 MidiStreamView
* const view
= mtv
->midi_view();
803 double note
= view
->y_to_note(y
);
806 assert(note
<= 127.0);
808 // Start of note in frames relative to region start
809 framepos_t
const start_frames
= snap_frame_to_frame(trackview
.editor().pixel_to_frame(x
));
810 assert(start_frames
>= 0);
813 length
= frames_to_beats(
814 snap_frame_to_frame(start_frames
+ beats_to_frames(length
)) - start_frames
);
816 assert (length
!= 0);
819 length
= frames_to_beats (beats_to_frames (length
) - 1);
822 const boost::shared_ptr
<NoteType
> new_note (new NoteType (get_channel_for_add (),
823 frames_to_beats(start_frames
+ _region
->start()), length
,
824 (uint8_t)note
, 0x40));
826 if (_model
->contains (new_note
)) {
830 view
->update_note_range(new_note
->note());
832 MidiModel::NoteDiffCommand
* cmd
= _model
->new_note_diff_command("add note");
834 _model
->apply_command(*trackview
.session(), cmd
);
836 play_midi_note (new_note
);
840 MidiRegionView::clear_events()
845 for (std::vector
<GhostRegion
*>::iterator g
= ghosts
.begin(); g
!= ghosts
.end(); ++g
) {
846 if ((gr
= dynamic_cast<MidiGhostRegion
*>(*g
)) != 0) {
851 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
856 _patch_changes
.clear();
858 _optimization_iterator
= _events
.end();
862 MidiRegionView::display_model(boost::shared_ptr
<MidiModel
> model
)
866 content_connection
.disconnect ();
867 _model
->ContentsChanged
.connect (content_connection
, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model
, this), gui_context());
871 if (_enable_display
) {
877 MidiRegionView::start_note_diff_command (string name
)
879 if (!_note_diff_command
) {
880 _note_diff_command
= _model
->new_note_diff_command (name
);
885 MidiRegionView::note_diff_add_note (const boost::shared_ptr
<NoteType
> note
, bool selected
, bool show_velocity
)
887 if (_note_diff_command
) {
888 _note_diff_command
->add (note
);
891 _marked_for_selection
.insert(note
);
894 _marked_for_velocity
.insert(note
);
899 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent
* ev
)
901 if (_note_diff_command
&& ev
->note()) {
902 _note_diff_command
->remove(ev
->note());
907 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent
* ev
,
908 MidiModel::NoteDiffCommand::Property property
,
911 if (_note_diff_command
) {
912 _note_diff_command
->change (ev
->note(), property
, val
);
917 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent
* ev
,
918 MidiModel::NoteDiffCommand::Property property
,
919 Evoral::MusicalTime val
)
921 if (_note_diff_command
) {
922 _note_diff_command
->change (ev
->note(), property
, val
);
927 MidiRegionView::apply_diff ()
931 if (!_note_diff_command
) {
935 if ((add_or_remove
= _note_diff_command
->adds_or_removes())) {
936 // Mark all selected notes for selection when model reloads
937 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
938 _marked_for_selection
.insert((*i
)->note());
942 _model
->apply_command(*trackview
.session(), _note_diff_command
);
943 _note_diff_command
= 0;
944 midi_view()->midi_track()->playlist_modified();
947 _marked_for_selection
.clear();
950 _marked_for_velocity
.clear();
954 MidiRegionView::apply_diff_as_subcommand ()
958 if (!_note_diff_command
) {
962 if ((add_or_remove
= _note_diff_command
->adds_or_removes())) {
963 // Mark all selected notes for selection when model reloads
964 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
965 _marked_for_selection
.insert((*i
)->note());
969 _model
->apply_command_as_subcommand(*trackview
.session(), _note_diff_command
);
970 _note_diff_command
= 0;
971 midi_view()->midi_track()->playlist_modified();
974 _marked_for_selection
.clear();
976 _marked_for_velocity
.clear();
981 MidiRegionView::abort_command()
983 delete _note_diff_command
;
984 _note_diff_command
= 0;
989 MidiRegionView::find_canvas_note (boost::shared_ptr
<NoteType
> note
)
991 if (_optimization_iterator
!= _events
.end()) {
992 ++_optimization_iterator
;
995 if (_optimization_iterator
!= _events
.end() && (*_optimization_iterator
)->note() == note
) {
996 return *_optimization_iterator
;
999 for (_optimization_iterator
= _events
.begin(); _optimization_iterator
!= _events
.end(); ++_optimization_iterator
) {
1000 if ((*_optimization_iterator
)->note() == note
) {
1001 return *_optimization_iterator
;
1009 MidiRegionView::get_events (Events
& e
, Evoral::Sequence
<Evoral::MusicalTime
>::NoteOperator op
, uint8_t val
, int chan_mask
)
1011 MidiModel::Notes notes
;
1012 _model
->get_notes (notes
, op
, val
, chan_mask
);
1014 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1015 CanvasNoteEvent
* cne
= find_canvas_note (*n
);
1023 MidiRegionView::redisplay_model()
1025 // Don't redisplay the model if we're currently recording and displaying that
1026 if (_active_notes
) {
1031 cerr
<< "MidiRegionView::redisplay_model called without a model" << endmsg
;
1035 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1036 (*i
)->invalidate ();
1039 MidiModel::ReadLock
lock(_model
->read_lock());
1041 MidiModel::Notes
& notes (_model
->notes());
1042 _optimization_iterator
= _events
.begin();
1044 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1046 boost::shared_ptr
<NoteType
> note (*n
);
1047 CanvasNoteEvent
* cne
;
1050 if (note_in_region_range (note
, visible
)) {
1052 if ((cne
= find_canvas_note (note
)) != 0) {
1059 if ((cn
= dynamic_cast<CanvasNote
*>(cne
)) != 0) {
1061 } else if ((ch
= dynamic_cast<CanvasHit
*>(cne
)) != 0) {
1073 add_note (note
, visible
);
1078 if ((cne
= find_canvas_note (note
)) != 0) {
1086 /* remove note items that are no longer valid */
1088 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ) {
1089 if (!(*i
)->valid ()) {
1091 i
= _events
.erase (i
);
1097 _patch_changes
.clear();
1101 display_patch_changes ();
1103 _marked_for_selection
.clear ();
1104 _marked_for_velocity
.clear ();
1106 /* we may have caused _events to contain things out of order (e.g. if a note
1107 moved earlier or later). we don't generally need them in time order, but
1108 make a note that a sort is required for those cases that require it.
1111 _sort_needed
= true;
1115 MidiRegionView::display_patch_changes ()
1117 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
1118 uint16_t chn_mask
= mtv
->channel_selector().get_selected_channels();
1120 for (uint8_t i
= 0; i
< 16; ++i
) {
1121 if (chn_mask
& (1<<i
)) {
1122 display_patch_changes_on_channel (i
);
1128 MidiRegionView::display_patch_changes_on_channel (uint8_t channel
)
1130 for (MidiModel::PatchChanges::const_iterator i
= _model
->patch_changes().begin(); i
!= _model
->patch_changes().end(); ++i
) {
1132 if ((*i
)->channel() != channel
) {
1136 MIDI::Name::PatchPrimaryKey
patch_key ((*i
)->bank_msb (), (*i
)->bank_lsb (), (*i
)->program ());
1138 boost::shared_ptr
<MIDI::Name::Patch
> patch
=
1139 MIDI::Name::MidiPatchManager::instance().find_patch(
1140 _model_name
, _custom_device_mode
, channel
, patch_key
);
1143 add_canvas_patch_change (*i
, patch
->name());
1146 /* program and bank numbers are zero-based: convert to one-based */
1147 snprintf (buf
, 16, "%d\n%d", (*i
)->program() + 1, (*i
)->bank() + 1);
1148 add_canvas_patch_change (*i
, buf
);
1154 MidiRegionView::display_sysexes()
1156 for (MidiModel::SysExes::const_iterator i
= _model
->sysexes().begin(); i
!= _model
->sysexes().end(); ++i
) {
1157 Evoral::MusicalTime time
= (*i
)->time();
1162 for (uint32_t b
= 0; b
< (*i
)->size(); ++b
) {
1163 str
<< int((*i
)->buffer()[b
]);
1164 if (b
!= (*i
)->size() -1) {
1168 string text
= str
.str();
1170 const double x
= trackview
.editor().frame_to_pixel(beats_to_frames(time
));
1172 double height
= midi_stream_view()->contents_height();
1174 boost::shared_ptr
<CanvasSysEx
> sysex
= boost::shared_ptr
<CanvasSysEx
>(
1175 new CanvasSysEx(*this, *_note_group
, text
, height
, x
, 1.0));
1177 // Show unless patch change is beyond the region bounds
1178 if (time
- _region
->start() >= _region
->length() || time
< _region
->start()) {
1184 _sys_exes
.push_back(sysex
);
1189 MidiRegionView::~MidiRegionView ()
1191 in_destructor
= true;
1193 trackview
.editor().hide_verbose_canvas_cursor ();
1195 note_delete_connection
.disconnect ();
1197 delete _list_editor
;
1199 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1201 if (_active_notes
) {
1209 delete _note_diff_command
;
1210 delete _step_edit_cursor
;
1211 delete _temporary_note_group
;
1215 MidiRegionView::region_resized (const PropertyChange
& what_changed
)
1217 RegionView::region_resized(what_changed
);
1219 if (what_changed
.contains (ARDOUR::Properties::position
)) {
1220 set_duration(_region
->length(), 0);
1221 if (_enable_display
) {
1228 MidiRegionView::reset_width_dependent_items (double pixel_width
)
1230 RegionView::reset_width_dependent_items(pixel_width
);
1231 assert(_pixel_width
== pixel_width
);
1233 if (_enable_display
) {
1237 move_step_edit_cursor (_step_edit_cursor_position
);
1238 set_step_edit_cursor_width (_step_edit_cursor_width
);
1242 MidiRegionView::set_height (double height
)
1244 static const double FUDGE
= 2.0;
1245 const double old_height
= _height
;
1246 RegionView::set_height(height
);
1247 _height
= height
- FUDGE
;
1249 apply_note_range(midi_stream_view()->lowest_note(),
1250 midi_stream_view()->highest_note(),
1251 height
!= old_height
+ FUDGE
);
1254 name_pixbuf
->raise_to_top();
1257 for (PatchChanges::iterator x
= _patch_changes
.begin(); x
!= _patch_changes
.end(); ++x
) {
1258 (*x
)->set_height (midi_stream_view()->contents_height());
1261 if (_step_edit_cursor
) {
1262 _step_edit_cursor
->property_y2() = midi_stream_view()->contents_height();
1267 /** Apply the current note range from the stream view
1268 * by repositioning/hiding notes as necessary
1271 MidiRegionView::apply_note_range (uint8_t min
, uint8_t max
, bool force
)
1273 if (!_enable_display
) {
1277 if (!force
&& _current_range_min
== min
&& _current_range_max
== max
) {
1281 _current_range_min
= min
;
1282 _current_range_max
= max
;
1284 for (Events::const_iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1285 CanvasNoteEvent
* event
= *i
;
1286 boost::shared_ptr
<NoteType
> note (event
->note());
1288 if (note
->note() < _current_range_min
||
1289 note
->note() > _current_range_max
) {
1295 if (CanvasNote
* cnote
= dynamic_cast<CanvasNote
*>(event
)) {
1297 const double y1
= midi_stream_view()->note_to_y(note
->note());
1298 const double y2
= y1
+ floor(midi_stream_view()->note_height());
1300 cnote
->property_y1() = y1
;
1301 cnote
->property_y2() = y2
;
1303 } else if (CanvasHit
* chit
= dynamic_cast<CanvasHit
*>(event
)) {
1305 const double diamond_size
= update_hit (chit
);
1307 chit
->set_height (diamond_size
);
1313 MidiRegionView::add_ghost (TimeAxisView
& tv
)
1317 double unit_position
= _region
->position () / samples_per_unit
;
1318 MidiTimeAxisView
* mtv
= dynamic_cast<MidiTimeAxisView
*>(&tv
);
1319 MidiGhostRegion
* ghost
;
1321 if (mtv
&& mtv
->midi_view()) {
1322 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1323 to allow having midi notes on top of note lines and waveforms.
1325 ghost
= new MidiGhostRegion (*mtv
->midi_view(), trackview
, unit_position
);
1327 ghost
= new MidiGhostRegion (tv
, trackview
, unit_position
);
1330 ghost
->set_height ();
1331 ghost
->set_duration (_region
->length() / samples_per_unit
);
1332 ghosts
.push_back (ghost
);
1334 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1335 if ((note
= dynamic_cast<CanvasNote
*>(*i
)) != 0) {
1336 ghost
->add_note(note
);
1340 GhostRegion::CatchDeletion
.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost
, this, _1
), gui_context());
1346 /** Begin tracking note state for successive calls to add_event
1349 MidiRegionView::begin_write()
1351 assert(!_active_notes
);
1352 _active_notes
= new CanvasNote
*[128];
1353 for (unsigned i
=0; i
< 128; ++i
) {
1354 _active_notes
[i
] = 0;
1359 /** Destroy note state for add_event
1362 MidiRegionView::end_write()
1364 delete[] _active_notes
;
1366 _marked_for_selection
.clear();
1367 _marked_for_velocity
.clear();
1371 /** Resolve an active MIDI note (while recording).
1374 MidiRegionView::resolve_note(uint8_t note
, double end_time
)
1376 if (midi_view()->note_mode() != Sustained
) {
1380 if (_active_notes
&& _active_notes
[note
]) {
1382 const framepos_t end_time_frames
= beats_to_frames(end_time
);
1384 _active_notes
[note
]->property_x2() = trackview
.editor().frame_to_pixel(end_time_frames
);
1385 _active_notes
[note
]->property_outline_what() = (guint32
) 0xF; // all edges
1386 _active_notes
[note
] = 0;
1391 /** Extend active notes to rightmost edge of region (if length is changed)
1394 MidiRegionView::extend_active_notes()
1396 if (!_active_notes
) {
1400 for (unsigned i
=0; i
< 128; ++i
) {
1401 if (_active_notes
[i
]) {
1402 _active_notes
[i
]->property_x2() = trackview
.editor().frame_to_pixel(_region
->length());
1409 MidiRegionView::play_midi_note(boost::shared_ptr
<NoteType
> note
)
1411 if (no_sound_notes
|| !trackview
.editor().sound_notes()) {
1415 RouteUI
* route_ui
= dynamic_cast<RouteUI
*> (&trackview
);
1417 if (!route_ui
|| !route_ui
->midi_track()) {
1421 NotePlayer
* np
= new NotePlayer (route_ui
->midi_track());
1427 MidiRegionView::play_midi_chord (vector
<boost::shared_ptr
<NoteType
> > notes
)
1429 if (no_sound_notes
|| !trackview
.editor().sound_notes()) {
1433 RouteUI
* route_ui
= dynamic_cast<RouteUI
*> (&trackview
);
1435 if (!route_ui
|| !route_ui
->midi_track()) {
1439 NotePlayer
* np
= new NotePlayer (route_ui
->midi_track());
1441 for (vector
<boost::shared_ptr
<NoteType
> >::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1450 MidiRegionView::note_in_region_range(const boost::shared_ptr
<NoteType
> note
, bool& visible
) const
1452 const framepos_t note_start_frames
= beats_to_frames(note
->time());
1454 bool outside
= (note_start_frames
- _region
->start() >= _region
->length()) ||
1455 (note_start_frames
< _region
->start());
1457 visible
= (note
->note() >= midi_stream_view()->lowest_note()) &&
1458 (note
->note() <= midi_stream_view()->highest_note());
1464 MidiRegionView::update_note (CanvasNote
* ev
)
1466 boost::shared_ptr
<NoteType
> note
= ev
->note();
1468 const framepos_t note_start_frames
= beats_to_frames(note
->time());
1470 /* trim note display to not overlap the end of its region */
1471 const framepos_t note_end_frames
= min (beats_to_frames (note
->end_time()), _region
->start() + _region
->length());
1473 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1474 const double y1
= midi_stream_view()->note_to_y(note
->note());
1475 const double note_endpixel
= trackview
.editor().frame_to_pixel(note_end_frames
- _region
->start());
1477 ev
->property_x1() = x
;
1478 ev
->property_y1() = y1
;
1480 if (note
->length() > 0) {
1481 ev
->property_x2() = note_endpixel
;
1483 ev
->property_x2() = trackview
.editor().frame_to_pixel(_region
->length());
1486 ev
->property_y2() = y1
+ floor(midi_stream_view()->note_height());
1488 if (note
->length() == 0) {
1489 if (_active_notes
) {
1490 assert(note
->note() < 128);
1491 // If this note is already active there's a stuck note,
1492 // finish the old note rectangle
1493 if (_active_notes
[note
->note()]) {
1494 CanvasNote
* const old_rect
= _active_notes
[note
->note()];
1495 boost::shared_ptr
<NoteType
> old_note
= old_rect
->note();
1496 old_rect
->property_x2() = x
;
1497 old_rect
->property_outline_what() = (guint32
) 0xF;
1499 _active_notes
[note
->note()] = ev
;
1501 /* outline all but right edge */
1502 ev
->property_outline_what() = (guint32
) (0x1 & 0x4 & 0x8);
1504 /* outline all edges */
1505 ev
->property_outline_what() = (guint32
) 0xF;
1510 MidiRegionView::update_hit (CanvasHit
* ev
)
1512 boost::shared_ptr
<NoteType
> note
= ev
->note();
1514 const framepos_t note_start_frames
= beats_to_frames(note
->time());
1515 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1516 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1517 const double y
= midi_stream_view()->note_to_y(note
->note()) + ((diamond_size
-2) / 4.0);
1521 return diamond_size
;
1524 /** Add a MIDI note to the view (with length).
1526 * If in sustained mode, notes with length 0 will be considered active
1527 * notes, and resolve_note should be called when the corresponding note off
1528 * event arrives, to properly display the note.
1531 MidiRegionView::add_note(const boost::shared_ptr
<NoteType
> note
, bool visible
)
1533 CanvasNoteEvent
* event
= 0;
1535 assert(note
->time() >= 0);
1536 assert(midi_view()->note_mode() == Sustained
|| midi_view()->note_mode() == Percussive
);
1538 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1540 if (midi_view()->note_mode() == Sustained
) {
1542 CanvasNote
* ev_rect
= new CanvasNote(*this, *_note_group
, note
);
1544 update_note (ev_rect
);
1548 MidiGhostRegion
* gr
;
1550 for (std::vector
<GhostRegion
*>::iterator g
= ghosts
.begin(); g
!= ghosts
.end(); ++g
) {
1551 if ((gr
= dynamic_cast<MidiGhostRegion
*>(*g
)) != 0) {
1552 gr
->add_note(ev_rect
);
1556 } else if (midi_view()->note_mode() == Percussive
) {
1558 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1560 CanvasHit
* ev_diamond
= new CanvasHit(*this, *_note_group
, diamond_size
, note
);
1562 update_hit (ev_diamond
);
1571 if (_marked_for_selection
.find(note
) != _marked_for_selection
.end()) {
1572 note_selected(event
, true);
1575 if (_marked_for_velocity
.find(note
) != _marked_for_velocity
.end()) {
1576 event
->show_velocity();
1579 event
->on_channel_selection_change(_last_channel_selection
);
1580 _events
.push_back(event
);
1589 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
1590 MidiStreamView
* const view
= mtv
->midi_view();
1592 view
->update_note_range(note
->note());
1596 MidiRegionView::step_add_note (uint8_t channel
, uint8_t number
, uint8_t velocity
,
1597 Evoral::MusicalTime pos
, Evoral::MusicalTime len
)
1599 boost::shared_ptr
<NoteType
> new_note (new NoteType (channel
, pos
, len
, number
, velocity
));
1601 /* potentially extend region to hold new note */
1603 framepos_t end_frame
= _region
->position() + beats_to_frames (new_note
->end_time());
1604 framepos_t region_end
= _region
->position() + _region
->length() - 1;
1606 if (end_frame
> region_end
) {
1607 _region
->set_length (end_frame
- _region
->position(), this);
1610 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
1611 MidiStreamView
* const view
= mtv
->midi_view();
1613 view
->update_note_range(new_note
->note());
1615 _marked_for_selection
.clear ();
1618 start_note_diff_command (_("step add"));
1619 note_diff_add_note (new_note
, true, false);
1622 // last_step_edit_note = new_note;
1626 MidiRegionView::step_sustain (Evoral::MusicalTime beats
)
1628 change_note_lengths (false, false, beats
, false, true);
1632 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch
, const string
& displaytext
)
1634 assert (patch
->time() >= 0);
1636 const double x
= trackview
.editor().frame_to_pixel (beats_to_frames (patch
->time()));
1638 double const height
= midi_stream_view()->contents_height();
1640 boost::shared_ptr
<CanvasPatchChange
> patch_change
= boost::shared_ptr
<CanvasPatchChange
>(
1641 new CanvasPatchChange(*this, *_note_group
,
1646 _custom_device_mode
,
1650 // Show unless patch change is beyond the region bounds
1651 if (patch
->time() - _region
->start() >= _region
->length() || patch
->time() < _region
->start()) {
1652 patch_change
->hide();
1654 patch_change
->show();
1657 _patch_changes
.push_back (patch_change
);
1661 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time
, uint8_t channel
, MIDI::Name::PatchPrimaryKey
& key
)
1663 MidiModel::PatchChanges::iterator i
= _model
->patch_change_lower_bound (time
);
1664 while (i
!= _model
->patch_changes().end() && (*i
)->channel() != channel
) {
1668 if (i
!= _model
->patch_changes().end()) {
1669 key
.msb
= (*i
)->bank_msb ();
1670 key
.lsb
= (*i
)->bank_lsb ();
1671 key
.program_number
= (*i
)->program ();
1673 key
.msb
= key
.lsb
= key
.program_number
= 0;
1676 assert (key
.is_sane());
1681 MidiRegionView::change_patch_change (CanvasPatchChange
& pc
, const MIDI::Name::PatchPrimaryKey
& new_patch
)
1683 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("alter patch change"));
1685 if (pc
.patch()->program() != new_patch
.program_number
) {
1686 c
->change_program (pc
.patch (), new_patch
.program_number
);
1689 int const new_bank
= (new_patch
.msb
<< 7) | new_patch
.lsb
;
1690 if (pc
.patch()->bank() != new_bank
) {
1691 c
->change_bank (pc
.patch (), new_bank
);
1694 _model
->apply_command (*trackview
.session(), c
);
1696 _patch_changes
.clear ();
1697 display_patch_changes ();
1701 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change
, const Evoral::PatchChange
<Evoral::MusicalTime
> & new_change
)
1703 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("alter patch change"));
1705 if (old_change
->time() != new_change
.time()) {
1706 c
->change_time (old_change
, new_change
.time());
1709 if (old_change
->channel() != new_change
.channel()) {
1710 c
->change_channel (old_change
, new_change
.channel());
1713 if (old_change
->program() != new_change
.program()) {
1714 c
->change_program (old_change
, new_change
.program());
1717 if (old_change
->bank() != new_change
.bank()) {
1718 c
->change_bank (old_change
, new_change
.bank());
1721 _model
->apply_command (*trackview
.session(), c
);
1723 _patch_changes
.clear ();
1724 display_patch_changes ();
1727 /** Add a patch change to the region.
1728 * @param t Time in frames relative to region position
1729 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1730 * get_channel_for_add())
1733 MidiRegionView::add_patch_change (framecnt_t t
, Evoral::PatchChange
<Evoral::MusicalTime
> const & patch
)
1735 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("add patch change"));
1736 c
->add (MidiModel::PatchChangePtr (
1737 new Evoral::PatchChange
<Evoral::MusicalTime
> (
1738 frames_to_beats (t
+ midi_region()->start()), get_channel_for_add(), patch
.program(), patch
.bank()
1742 _model
->apply_command (*trackview
.session(), c
);
1744 _patch_changes
.clear ();
1745 display_patch_changes ();
1749 MidiRegionView::move_patch_change (CanvasPatchChange
& pc
, Evoral::MusicalTime t
)
1751 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("move patch change"));
1752 c
->change_time (pc
.patch (), t
);
1753 _model
->apply_command (*trackview
.session(), c
);
1755 _patch_changes
.clear ();
1756 display_patch_changes ();
1760 MidiRegionView::delete_patch_change (CanvasPatchChange
* pc
)
1762 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("delete patch change"));
1763 c
->remove (pc
->patch ());
1764 _model
->apply_command (*trackview
.session(), c
);
1766 _patch_changes
.clear ();
1767 display_patch_changes ();
1771 MidiRegionView::previous_patch (CanvasPatchChange
& patch
)
1773 if (patch
.patch()->program() < 127) {
1774 MIDI::Name::PatchPrimaryKey key
;
1775 get_patch_key_at (patch
.patch()->time(), patch
.patch()->channel(), key
);
1776 key
.program_number
++;
1777 change_patch_change (patch
, key
);
1782 MidiRegionView::next_patch (CanvasPatchChange
& patch
)
1784 if (patch
.patch()->program() > 0) {
1785 MIDI::Name::PatchPrimaryKey key
;
1786 get_patch_key_at (patch
.patch()->time(), patch
.patch()->channel(), key
);
1787 key
.program_number
--;
1788 change_patch_change (patch
, key
);
1793 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent
* cne
)
1795 if (_selection
.empty()) {
1799 if (_selection
.erase (cne
) > 0) {
1800 cerr
<< "Erased a CNE from selection\n";
1805 MidiRegionView::delete_selection()
1807 if (_selection
.empty()) {
1811 start_note_diff_command (_("delete selection"));
1813 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1814 if ((*i
)->selected()) {
1815 _note_diff_command
->remove((*i
)->note());
1825 MidiRegionView::delete_note (boost::shared_ptr
<NoteType
> n
)
1827 start_note_diff_command (_("delete note"));
1828 _note_diff_command
->remove (n
);
1831 trackview
.editor().hide_verbose_canvas_cursor ();
1835 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent
* ev
)
1837 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1838 if ((*i
)->selected() && (*i
) != ev
) {
1839 (*i
)->set_selected(false);
1840 (*i
)->hide_velocity();
1848 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent
* ev
)
1850 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
1853 Selection::iterator tmp
= i
;
1856 (*i
)->set_selected (false);
1857 _selection
.erase (i
);
1866 /* don't bother with removing this regionview from the editor selection,
1867 since we're about to add another note, and thus put/keep this
1868 regionview in the editor selection.
1871 if (!ev
->selected()) {
1872 add_to_selection (ev
);
1877 MidiRegionView::select_all_notes ()
1881 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1882 add_to_selection (*i
);
1887 MidiRegionView::select_matching_notes (uint8_t notenum
, uint16_t channel_mask
, bool add
, bool extend
)
1889 uint8_t low_note
= 127;
1890 uint8_t high_note
= 0;
1891 MidiModel::Notes
& notes (_model
->notes());
1892 _optimization_iterator
= _events
.begin();
1898 if (extend
&& _selection
.empty()) {
1904 /* scan existing selection to get note range */
1906 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1907 if ((*i
)->note()->note() < low_note
) {
1908 low_note
= (*i
)->note()->note();
1910 if ((*i
)->note()->note() > high_note
) {
1911 high_note
= (*i
)->note()->note();
1915 low_note
= min (low_note
, notenum
);
1916 high_note
= max (high_note
, notenum
);
1919 no_sound_notes
= true;
1921 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1923 boost::shared_ptr
<NoteType
> note (*n
);
1924 CanvasNoteEvent
* cne
;
1925 bool select
= false;
1927 if (((1 << note
->channel()) & channel_mask
) != 0) {
1929 if ((note
->note() >= low_note
&& note
->note() <= high_note
)) {
1932 } else if (note
->note() == notenum
) {
1938 if ((cne
= find_canvas_note (note
)) != 0) {
1939 // extend is false because we've taken care of it,
1940 // since it extends by time range, not pitch.
1941 note_selected (cne
, add
, false);
1945 add
= true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1949 no_sound_notes
= false;
1953 MidiRegionView::toggle_matching_notes (uint8_t notenum
, uint16_t channel_mask
)
1955 MidiModel::Notes
& notes (_model
->notes());
1956 _optimization_iterator
= _events
.begin();
1958 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1960 boost::shared_ptr
<NoteType
> note (*n
);
1961 CanvasNoteEvent
* cne
;
1963 if (note
->note() == notenum
&& (((0x0001 << note
->channel()) & channel_mask
) != 0)) {
1964 if ((cne
= find_canvas_note (note
)) != 0) {
1965 if (cne
->selected()) {
1966 note_deselected (cne
);
1968 note_selected (cne
, true, false);
1976 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent
* ev
, bool add
, bool extend
)
1979 clear_selection_except(ev
);
1984 if (!ev
->selected()) {
1985 add_to_selection (ev
);
1989 /* find end of latest note selected, select all between that and the start of "ev" */
1991 Evoral::MusicalTime earliest
= Evoral::MaxMusicalTime
;
1992 Evoral::MusicalTime latest
= 0;
1994 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1995 if ((*i
)->note()->end_time() > latest
) {
1996 latest
= (*i
)->note()->end_time();
1998 if ((*i
)->note()->time() < earliest
) {
1999 earliest
= (*i
)->note()->time();
2003 if (ev
->note()->end_time() > latest
) {
2004 latest
= ev
->note()->end_time();
2007 if (ev
->note()->time() < earliest
) {
2008 earliest
= ev
->note()->time();
2011 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2013 /* find notes entirely within OR spanning the earliest..latest range */
2015 if (((*i
)->note()->time() >= earliest
&& (*i
)->note()->end_time() <= latest
) ||
2016 ((*i
)->note()->time() <= earliest
&& (*i
)->note()->end_time() >= latest
)) {
2017 add_to_selection (*i
);
2021 /* if events were guaranteed to be time sorted, we could do this.
2022 but as of sept 10th 2009, they no longer are.
2025 if ((*i
)->note()->time() > latest
) {
2034 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent
* ev
)
2036 remove_from_selection (ev
);
2040 MidiRegionView::update_drag_selection(double x1
, double x2
, double y1
, double y2
)
2050 // TODO: Make this faster by storing the last updated selection rect, and only
2051 // adjusting things that are in the area that appears/disappeared.
2052 // We probably need a tree to be able to find events in O(log(n)) time.
2054 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2056 /* check if any corner of the note is inside the rect
2059 1) this is computing "touched by", not "contained by" the rect.
2060 2) this does not require that events be sorted in time.
2063 const double ix1
= (*i
)->x1();
2064 const double ix2
= (*i
)->x2();
2065 const double iy1
= (*i
)->y1();
2066 const double iy2
= (*i
)->y2();
2068 if ((ix1
>= x1
&& ix1
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
2069 (ix1
>= x1
&& ix1
<= x2
&& iy2
>= y1
&& iy2
<= y2
) ||
2070 (ix2
>= x1
&& ix2
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
2071 (ix2
>= x1
&& ix2
<= x2
&& iy2
>= y1
&& iy2
<= y2
)) {
2074 if (!(*i
)->selected()) {
2075 add_to_selection (*i
);
2077 } else if ((*i
)->selected()) {
2078 // Not inside rectangle
2079 remove_from_selection (*i
);
2085 MidiRegionView::remove_from_selection (CanvasNoteEvent
* ev
)
2087 Selection::iterator i
= _selection
.find (ev
);
2089 if (i
!= _selection
.end()) {
2090 _selection
.erase (i
);
2093 ev
->set_selected (false);
2094 ev
->hide_velocity ();
2096 if (_selection
.empty()) {
2097 PublicEditor
& editor (trackview
.editor());
2098 editor
.get_selection().remove (this);
2103 MidiRegionView::add_to_selection (CanvasNoteEvent
* ev
)
2105 bool add_mrv_selection
= false;
2107 if (_selection
.empty()) {
2108 add_mrv_selection
= true;
2111 if (_selection
.insert (ev
).second
) {
2112 ev
->set_selected (true);
2113 play_midi_note ((ev
)->note());
2116 if (add_mrv_selection
) {
2117 PublicEditor
& editor (trackview
.editor());
2118 editor
.get_selection().add (this);
2123 MidiRegionView::move_selection(double dx
, double dy
, double cumulative_dy
)
2125 typedef vector
<boost::shared_ptr
<NoteType
> > PossibleChord
;
2126 PossibleChord to_play
;
2127 Evoral::MusicalTime earliest
= Evoral::MaxMusicalTime
;
2129 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2130 if ((*i
)->note()->time() < earliest
) {
2131 earliest
= (*i
)->note()->time();
2135 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2136 if (Evoral::musical_time_equal ((*i
)->note()->time(), earliest
)) {
2137 to_play
.push_back ((*i
)->note());
2139 (*i
)->move_event(dx
, dy
);
2142 if (dy
&& !_selection
.empty() && !no_sound_notes
&& trackview
.editor().sound_notes()) {
2144 if (to_play
.size() > 1) {
2146 PossibleChord shifted
;
2148 for (PossibleChord::iterator n
= to_play
.begin(); n
!= to_play
.end(); ++n
) {
2149 boost::shared_ptr
<NoteType
> moved_note (new NoteType (**n
));
2150 moved_note
->set_note (moved_note
->note() + cumulative_dy
);
2151 shifted
.push_back (moved_note
);
2154 play_midi_chord (shifted
);
2156 } else if (!to_play
.empty()) {
2158 boost::shared_ptr
<NoteType
> moved_note (new NoteType (*to_play
.front()));
2159 moved_note
->set_note (moved_note
->note() + cumulative_dy
);
2160 play_midi_note (moved_note
);
2166 MidiRegionView::note_dropped(CanvasNoteEvent
*, frameoffset_t dt
, int8_t dnote
)
2168 assert (!_selection
.empty());
2170 uint8_t lowest_note_in_selection
= 127;
2171 uint8_t highest_note_in_selection
= 0;
2172 uint8_t highest_note_difference
= 0;
2174 // find highest and lowest notes first
2176 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2177 uint8_t pitch
= (*i
)->note()->note();
2178 lowest_note_in_selection
= std::min(lowest_note_in_selection
, pitch
);
2179 highest_note_in_selection
= std::max(highest_note_in_selection
, pitch
);
2183 cerr << "dnote: " << (int) dnote << endl;
2184 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2185 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2186 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2187 << int(highest_note_in_selection) << endl;
2188 cerr << "selection size: " << _selection.size() << endl;
2189 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2192 // Make sure the note pitch does not exceed the MIDI standard range
2193 if (highest_note_in_selection
+ dnote
> 127) {
2194 highest_note_difference
= highest_note_in_selection
- 127;
2197 start_note_diff_command (_("move notes"));
2199 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end() ; ++i
) {
2201 Evoral::MusicalTime new_time
= frames_to_beats (beats_to_frames ((*i
)->note()->time()) + dt
);
2207 note_diff_add_change (*i
, MidiModel::NoteDiffCommand::StartTime
, new_time
);
2209 uint8_t original_pitch
= (*i
)->note()->note();
2210 uint8_t new_pitch
= original_pitch
+ dnote
- highest_note_difference
;
2212 // keep notes in standard midi range
2213 clamp_to_0_127(new_pitch
);
2215 // keep original pitch if note is dragged outside valid midi range
2216 if ((original_pitch
!= 0 && new_pitch
== 0)
2217 || (original_pitch
!= 127 && new_pitch
== 127)) {
2218 new_pitch
= original_pitch
;
2221 lowest_note_in_selection
= std::min(lowest_note_in_selection
, new_pitch
);
2222 highest_note_in_selection
= std::max(highest_note_in_selection
, new_pitch
);
2224 note_diff_add_change (*i
, MidiModel::NoteDiffCommand::NoteNumber
, new_pitch
);
2229 // care about notes being moved beyond the upper/lower bounds on the canvas
2230 if (lowest_note_in_selection
< midi_stream_view()->lowest_note() ||
2231 highest_note_in_selection
> midi_stream_view()->highest_note()) {
2232 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange
);
2237 MidiRegionView::snap_pixel_to_frame(double x
)
2239 PublicEditor
& editor
= trackview
.editor();
2240 // x is region relative, convert it to global absolute frames
2241 framepos_t frame
= editor
.pixel_to_frame(x
) + _region
->position();
2242 editor
.snap_to(frame
);
2243 return frame
- _region
->position(); // convert back to region relative
2247 MidiRegionView::snap_frame_to_frame(framepos_t x
)
2249 PublicEditor
& editor
= trackview
.editor();
2250 // x is region relative, convert it to global absolute frames
2251 framepos_t frame
= x
+ _region
->position();
2252 editor
.snap_to(frame
);
2253 return frame
- _region
->position(); // convert back to region relative
2257 MidiRegionView::snap_to_pixel(double x
)
2259 return (double) trackview
.editor().frame_to_pixel(snap_pixel_to_frame(x
));
2263 MidiRegionView::get_position_pixels()
2265 framepos_t region_frame
= get_position();
2266 return trackview
.editor().frame_to_pixel(region_frame
);
2270 MidiRegionView::get_end_position_pixels()
2272 framepos_t frame
= get_position() + get_duration ();
2273 return trackview
.editor().frame_to_pixel(frame
);
2277 MidiRegionView::beats_to_frames(double beats
) const
2279 return _time_converter
.to(beats
);
2283 MidiRegionView::frames_to_beats(framepos_t frames
) const
2285 return _time_converter
.from(frames
);
2289 MidiRegionView::begin_resizing (bool /*at_front*/)
2291 _resize_data
.clear();
2293 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2294 CanvasNote
*note
= dynamic_cast<CanvasNote
*> (*i
);
2296 // only insert CanvasNotes into the map
2298 NoteResizeData
*resize_data
= new NoteResizeData();
2299 resize_data
->canvas_note
= note
;
2301 // create a new SimpleRect from the note which will be the resize preview
2302 SimpleRect
*resize_rect
= new SimpleRect(
2303 *_note_group
, note
->x1(), note
->y1(), note
->x2(), note
->y2());
2305 // calculate the colors: get the color settings
2306 uint32_t fill_color
= UINT_RGBA_CHANGE_A(
2307 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get(),
2310 // make the resize preview notes more transparent and bright
2311 fill_color
= UINT_INTERPOLATE(fill_color
, 0xFFFFFF40, 0.5);
2313 // calculate color based on note velocity
2314 resize_rect
->property_fill_color_rgba() = UINT_INTERPOLATE(
2315 CanvasNoteEvent::meter_style_fill_color(note
->note()->velocity(), note
->selected()),
2319 resize_rect
->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2320 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get());
2322 resize_data
->resize_rect
= resize_rect
;
2323 _resize_data
.push_back(resize_data
);
2328 /** Update resizing notes while user drags.
2329 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2330 * @param at_front which end of the note (true == note on, false == note off)
2331 * @param delta_x change in mouse position since the start of the drag
2332 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2333 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2334 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2335 * as the \a primary note.
2338 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent
* primary
, bool at_front
, double delta_x
, bool relative
)
2340 bool cursor_set
= false;
2342 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2343 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2344 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2349 current_x
= canvas_note
->x1() + delta_x
;
2351 current_x
= primary
->x1() + delta_x
;
2355 current_x
= canvas_note
->x2() + delta_x
;
2357 current_x
= primary
->x2() + delta_x
;
2362 resize_rect
->property_x1() = snap_to_pixel(current_x
);
2363 resize_rect
->property_x2() = canvas_note
->x2();
2365 resize_rect
->property_x2() = snap_to_pixel(current_x
);
2366 resize_rect
->property_x1() = canvas_note
->x1();
2372 beats
= snap_pixel_to_frame (current_x
);
2373 beats
= frames_to_beats (beats
);
2378 if (beats
< canvas_note
->note()->end_time()) {
2379 len
= canvas_note
->note()->time() - beats
;
2380 len
+= canvas_note
->note()->length();
2385 if (beats
>= canvas_note
->note()->time()) {
2386 len
= beats
- canvas_note
->note()->time();
2393 snprintf (buf
, sizeof (buf
), "%.3g beats", len
);
2394 trackview
.editor().show_verbose_canvas_cursor_with (buf
);
2403 /** Finish resizing notes when the user releases the mouse button.
2404 * Parameters the same as for \a update_resizing().
2407 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent
* primary
, bool at_front
, double delta_x
, bool relative
)
2409 start_note_diff_command (_("resize notes"));
2411 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2412 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2413 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2418 current_x
= canvas_note
->x1() + delta_x
;
2420 current_x
= primary
->x1() + delta_x
;
2424 current_x
= canvas_note
->x2() + delta_x
;
2426 current_x
= primary
->x2() + delta_x
;
2430 current_x
= snap_pixel_to_frame (current_x
);
2431 current_x
= frames_to_beats (current_x
);
2433 if (at_front
&& current_x
< canvas_note
->note()->end_time()) {
2434 note_diff_add_change (canvas_note
, MidiModel::NoteDiffCommand::StartTime
, current_x
);
2436 double len
= canvas_note
->note()->time() - current_x
;
2437 len
+= canvas_note
->note()->length();
2440 /* XXX convert to beats */
2441 note_diff_add_change (canvas_note
, MidiModel::NoteDiffCommand::Length
, len
);
2446 double len
= current_x
- canvas_note
->note()->time();
2449 /* XXX convert to beats */
2450 note_diff_add_change (canvas_note
, MidiModel::NoteDiffCommand::Length
, len
);
2458 _resize_data
.clear();
2463 MidiRegionView::change_note_channel (CanvasNoteEvent
* event
, int8_t channel
)
2465 note_diff_add_change (event
, MidiModel::NoteDiffCommand::Channel
, (uint8_t) channel
);
2469 MidiRegionView::change_note_velocity(CanvasNoteEvent
* event
, int8_t velocity
, bool relative
)
2471 uint8_t new_velocity
;
2474 new_velocity
= event
->note()->velocity() + velocity
;
2475 clamp_to_0_127(new_velocity
);
2477 new_velocity
= velocity
;
2480 event
->set_selected (event
->selected()); // change color
2482 note_diff_add_change (event
, MidiModel::NoteDiffCommand::Velocity
, new_velocity
);
2486 MidiRegionView::change_note_note (CanvasNoteEvent
* event
, int8_t note
, bool relative
)
2491 new_note
= event
->note()->note() + note
;
2496 clamp_to_0_127 (new_note
);
2497 note_diff_add_change (event
, MidiModel::NoteDiffCommand::NoteNumber
, new_note
);
2501 MidiRegionView::trim_note (CanvasNoteEvent
* event
, Evoral::MusicalTime front_delta
, Evoral::MusicalTime end_delta
)
2503 bool change_start
= false;
2504 bool change_length
= false;
2505 Evoral::MusicalTime new_start
= 0;
2506 Evoral::MusicalTime new_length
= 0;
2508 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2510 front_delta: if positive - move the start of the note later in time (shortening it)
2511 if negative - move the start of the note earlier in time (lengthening it)
2513 end_delta: if positive - move the end of the note later in time (lengthening it)
2514 if negative - move the end of the note earlier in time (shortening it)
2518 if (front_delta
< 0) {
2520 if (event
->note()->time() < -front_delta
) {
2523 new_start
= event
->note()->time() + front_delta
; // moves earlier
2526 /* start moved toward zero, so move the end point out to where it used to be.
2527 Note that front_delta is negative, so this increases the length.
2530 new_length
= event
->note()->length() - front_delta
;
2531 change_start
= true;
2532 change_length
= true;
2536 Evoral::MusicalTime new_pos
= event
->note()->time() + front_delta
;
2538 if (new_pos
< event
->note()->end_time()) {
2539 new_start
= event
->note()->time() + front_delta
;
2540 /* start moved toward the end, so move the end point back to where it used to be */
2541 new_length
= event
->note()->length() - front_delta
;
2542 change_start
= true;
2543 change_length
= true;
2550 bool can_change
= true;
2551 if (end_delta
< 0) {
2552 if (event
->note()->length() < -end_delta
) {
2558 new_length
= event
->note()->length() + end_delta
;
2559 change_length
= true;
2564 note_diff_add_change (event
, MidiModel::NoteDiffCommand::StartTime
, new_start
);
2567 if (change_length
) {
2568 note_diff_add_change (event
, MidiModel::NoteDiffCommand::Length
, new_length
);
2573 MidiRegionView::change_note_time (CanvasNoteEvent
* event
, Evoral::MusicalTime delta
, bool relative
)
2575 Evoral::MusicalTime new_time
;
2579 if (event
->note()->time() < -delta
) {
2582 new_time
= event
->note()->time() + delta
;
2585 new_time
= event
->note()->time() + delta
;
2591 note_diff_add_change (event
, MidiModel::NoteDiffCommand::StartTime
, new_time
);
2595 MidiRegionView::change_note_length (CanvasNoteEvent
* event
, Evoral::MusicalTime t
)
2597 note_diff_add_change (event
, MidiModel::NoteDiffCommand::Length
, t
);
2601 MidiRegionView::change_velocities (bool up
, bool fine
, bool allow_smush
)
2605 if (_selection
.empty()) {
2620 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2621 if ((*i
)->note()->velocity() + delta
== 0 || (*i
)->note()->velocity() + delta
== 127) {
2627 start_note_diff_command (_("change velocities"));
2629 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end();) {
2630 Selection::iterator next
= i
;
2632 change_note_velocity (*i
, delta
, true);
2638 if (!_selection
.empty()) {
2640 snprintf (buf
, sizeof (buf
), "Vel %d",
2641 (int) (*_selection
.begin())->note()->velocity());
2642 trackview
.editor().show_verbose_canvas_cursor_with (buf
, 10, 10);
2648 MidiRegionView::transpose (bool up
, bool fine
, bool allow_smush
)
2650 if (_selection
.empty()) {
2667 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2669 if ((int8_t) (*i
)->note()->note() + delta
<= 0) {
2673 if ((int8_t) (*i
)->note()->note() + delta
> 127) {
2680 start_note_diff_command (_("transpose"));
2682 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2683 Selection::iterator next
= i
;
2685 change_note_note (*i
, delta
, true);
2693 MidiRegionView::change_note_lengths (bool fine
, bool shorter
, Evoral::MusicalTime delta
, bool start
, bool end
)
2699 /* grab the current grid distance */
2701 delta
= trackview
.editor().get_grid_type_as_beats (success
, _region
->position());
2703 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2704 cerr
<< "Grid type not available as beats - TO BE FIXED\n";
2714 start_note_diff_command (_("change note lengths"));
2716 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2717 Selection::iterator next
= i
;
2720 /* note the negation of the delta for start */
2722 trim_note (*i
, (start
? -delta
: 0), (end
? delta
: 0));
2731 MidiRegionView::nudge_notes (bool forward
)
2733 if (_selection
.empty()) {
2737 /* pick a note as the point along the timeline to get the nudge distance.
2738 its not necessarily the earliest note, so we may want to pull the notes out
2739 into a vector and sort before using the first one.
2742 framepos_t ref_point
= _region
->position() + beats_to_frames ((*(_selection
.begin()))->note()->time());
2744 framepos_t distance
;
2746 if (trackview
.editor().snap_mode() == Editing::SnapOff
) {
2748 /* grid is off - use nudge distance */
2750 distance
= trackview
.editor().get_nudge_distance (ref_point
, unused
);
2756 framepos_t next_pos
= ref_point
;
2759 if (max_framepos
- 1 < next_pos
) {
2763 if (next_pos
== 0) {
2769 trackview
.editor().snap_to (next_pos
, (forward
? 1 : -1), false);
2770 distance
= ref_point
- next_pos
;
2773 if (distance
== 0) {
2777 Evoral::MusicalTime delta
= frames_to_beats (fabs (distance
));
2783 start_note_diff_command (_("nudge"));
2785 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2786 Selection::iterator next
= i
;
2788 change_note_time (*i
, delta
, true);
2796 MidiRegionView::change_channel(uint8_t channel
)
2798 start_note_diff_command(_("change channel"));
2799 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2800 note_diff_add_change (*i
, MidiModel::NoteDiffCommand::Channel
, channel
);
2808 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent
* ev
)
2810 Editor
* editor
= dynamic_cast<Editor
*>(&trackview
.editor());
2812 pre_enter_cursor
= editor
->get_canvas_cursor ();
2814 if (_mouse_state
== SelectTouchDragging
) {
2815 note_selected (ev
, true);
2818 show_verbose_canvas_cursor (ev
->note ());
2822 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent
*)
2824 Editor
* editor
= dynamic_cast<Editor
*>(&trackview
.editor());
2826 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2827 (*i
)->hide_velocity ();
2830 editor
->hide_verbose_canvas_cursor ();
2832 if (pre_enter_cursor
) {
2833 editor
->set_canvas_cursor (pre_enter_cursor
);
2834 pre_enter_cursor
= 0;
2839 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange
* ev
)
2842 s
<< ((int) ev
->patch()->program() + 1) << ":" << (ev
->patch()->bank() + 1);
2843 trackview
.editor().show_verbose_canvas_cursor_with (s
.str().c_str(), 10, 20);
2847 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange
*)
2849 trackview
.editor().hide_verbose_canvas_cursor ();
2853 MidiRegionView::note_mouse_position (float x_fraction
, float /*y_fraction*/, bool can_set_cursor
)
2855 Editor
* editor
= dynamic_cast<Editor
*>(&trackview
.editor());
2857 if (x_fraction
> 0.0 && x_fraction
< 0.25) {
2858 editor
->set_canvas_cursor (editor
->cursors()->left_side_trim
);
2859 } else if (x_fraction
>= 0.75 && x_fraction
< 1.0) {
2860 editor
->set_canvas_cursor (editor
->cursors()->right_side_trim
);
2862 if (pre_enter_cursor
&& can_set_cursor
) {
2863 editor
->set_canvas_cursor (pre_enter_cursor
);
2869 MidiRegionView::set_frame_color()
2872 if (_selected
&& should_show_selection
) {
2873 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase
.get();
2875 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase
.get();
2881 MidiRegionView::midi_channel_mode_changed(ChannelMode mode
, uint16_t mask
)
2885 case FilterChannels
:
2886 _force_channel
= -1;
2889 _force_channel
= mask
;
2890 mask
= 0xFFFF; // Show all notes as active (below)
2893 // Update notes for selection
2894 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2895 (*i
)->on_channel_selection_change(mask
);
2898 _last_channel_selection
= mask
;
2900 _patch_changes
.clear ();
2901 display_patch_changes ();
2905 MidiRegionView::midi_patch_settings_changed(std::string model
, std::string custom_device_mode
)
2907 _model_name
= model
;
2908 _custom_device_mode
= custom_device_mode
;
2913 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op
)
2915 if (_selection
.empty()) {
2919 PublicEditor
& editor (trackview
.editor());
2924 editor
.get_cut_buffer().add (selection_as_cut_buffer());
2932 start_note_diff_command();
2934 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2940 note_diff_remove_note (*i
);
2950 MidiRegionView::selection_as_cut_buffer () const
2954 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2955 NoteType
* n
= (*i
)->note().get();
2956 notes
.insert (boost::shared_ptr
<NoteType
> (new NoteType (*n
)));
2959 MidiCutBuffer
* cb
= new MidiCutBuffer (trackview
.session());
2966 MidiRegionView::paste (framepos_t pos
, float times
, const MidiCutBuffer
& mcb
)
2972 start_note_diff_command (_("paste"));
2974 Evoral::MusicalTime beat_delta
;
2975 Evoral::MusicalTime paste_pos_beats
;
2976 Evoral::MusicalTime duration
;
2977 Evoral::MusicalTime end_point
= 0;
2979 duration
= (*mcb
.notes().rbegin())->end_time() - (*mcb
.notes().begin())->time();
2980 paste_pos_beats
= frames_to_beats (pos
- _region
->position());
2981 beat_delta
= (*mcb
.notes().begin())->time() - paste_pos_beats
;
2982 paste_pos_beats
= 0;
2986 for (int n
= 0; n
< (int) times
; ++n
) {
2988 for (Notes::const_iterator i
= mcb
.notes().begin(); i
!= mcb
.notes().end(); ++i
) {
2990 boost::shared_ptr
<NoteType
> copied_note (new NoteType (*((*i
).get())));
2991 copied_note
->set_time (paste_pos_beats
+ copied_note
->time() - beat_delta
);
2993 /* make all newly added notes selected */
2995 note_diff_add_note (copied_note
, true);
2996 end_point
= copied_note
->end_time();
2999 paste_pos_beats
+= duration
;
3002 /* if we pasted past the current end of the region, extend the region */
3004 framepos_t end_frame
= _region
->position() + beats_to_frames (end_point
);
3005 framepos_t region_end
= _region
->position() + _region
->length() - 1;
3007 if (end_frame
> region_end
) {
3009 trackview
.session()->begin_reversible_command (_("paste"));
3011 _region
->clear_changes ();
3012 _region
->set_length (end_frame
, this);
3013 trackview
.session()->add_command (new StatefulDiffCommand (_region
));
3019 struct EventNoteTimeEarlyFirstComparator
{
3020 bool operator() (CanvasNoteEvent
* a
, CanvasNoteEvent
* b
) {
3021 return a
->note()->time() < b
->note()->time();
3026 MidiRegionView::time_sort_events ()
3028 if (!_sort_needed
) {
3032 EventNoteTimeEarlyFirstComparator cmp
;
3035 _sort_needed
= false;
3039 MidiRegionView::goto_next_note ()
3041 // framepos_t pos = -1;
3042 bool use_next
= false;
3044 if (_events
.back()->selected()) {
3048 time_sort_events ();
3050 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
3051 if ((*i
)->selected()) {
3054 } else if (use_next
) {
3056 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3061 /* use the first one */
3063 unique_select (_events
.front());
3068 MidiRegionView::goto_previous_note ()
3070 // framepos_t pos = -1;
3071 bool use_next
= false;
3073 if (_events
.front()->selected()) {
3077 time_sort_events ();
3079 for (Events::reverse_iterator i
= _events
.rbegin(); i
!= _events
.rend(); ++i
) {
3080 if ((*i
)->selected()) {
3083 } else if (use_next
) {
3085 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3090 /* use the last one */
3092 unique_select (*(_events
.rbegin()));
3096 MidiRegionView::selection_as_notelist (Notes
& selected
, bool allow_all_if_none_selected
)
3098 bool had_selected
= false;
3100 time_sort_events ();
3102 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
3103 if ((*i
)->selected()) {
3104 selected
.insert ((*i
)->note());
3105 had_selected
= true;
3109 if (allow_all_if_none_selected
&& !had_selected
) {
3110 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
3111 selected
.insert ((*i
)->note());
3117 MidiRegionView::update_ghost_note (double x
, double y
)
3122 _note_group
->w2i (x
, y
);
3123 framepos_t f
= trackview
.editor().pixel_to_frame (x
) + _region
->position ();
3124 trackview
.editor().snap_to (f
);
3125 f
-= _region
->position ();
3128 Evoral::MusicalTime beats
= trackview
.editor().get_grid_type_as_beats (success
, f
);
3134 double length
= frames_to_beats (snap_frame_to_frame (f
+ beats_to_frames (beats
)) - f
);
3136 _ghost_note
->note()->set_time (frames_to_beats (f
+ _region
->start()));
3137 _ghost_note
->note()->set_length (length
);
3138 _ghost_note
->note()->set_note (midi_stream_view()->y_to_note (y
));
3140 update_note (_ghost_note
);
3142 show_verbose_canvas_cursor (_ghost_note
->note ());
3146 MidiRegionView::create_ghost_note (double x
, double y
)
3151 boost::shared_ptr
<NoteType
> g (new NoteType
);
3152 _ghost_note
= new NoEventCanvasNote (*this, *_note_group
, g
);
3153 update_ghost_note (x
, y
);
3154 _ghost_note
->show ();
3159 show_verbose_canvas_cursor (_ghost_note
->note ());
3163 MidiRegionView::snap_changed ()
3169 create_ghost_note (_last_ghost_x
, _last_ghost_y
);
3173 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr
<NoteType
> n
) const
3176 snprintf (buf
, sizeof (buf
), "%s (%d)\nVel %d",
3177 Evoral::midi_note_name (n
->note()).c_str(),
3179 (int) n
->velocity());
3180 trackview
.editor().show_verbose_canvas_cursor_with (buf
, 10, 20);
3184 MidiRegionView::drop_down_keys ()
3186 _mouse_state
= None
;
3190 MidiRegionView::maybe_select_by_position (GdkEventButton
* ev
, double /*x*/, double y
)
3192 double note
= midi_stream_view()->y_to_note(y
);
3194 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
3196 uint16_t chn_mask
= mtv
->channel_selector().get_selected_channels();
3198 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::TertiaryModifier
)) {
3199 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchGreaterThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
3200 } else if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
3201 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchLessThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
3206 bool add_mrv_selection
= false;
3208 if (_selection
.empty()) {
3209 add_mrv_selection
= true;
3212 for (Events::iterator i
= e
.begin(); i
!= e
.end(); ++i
) {
3213 if (_selection
.insert (*i
).second
) {
3214 (*i
)->set_selected (true);
3218 if (add_mrv_selection
) {
3219 PublicEditor
& editor (trackview
.editor());
3220 editor
.get_selection().add (this);
3225 MidiRegionView::color_handler ()
3227 RegionView::color_handler ();
3229 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
3230 (*i
)->set_selected ((*i
)->selected()); // will change color
3233 /* XXX probably more to do here */
3237 MidiRegionView::enable_display (bool yn
)
3239 RegionView::enable_display (yn
);
3246 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos
)
3248 if (_step_edit_cursor
== 0) {
3249 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
3251 _step_edit_cursor
= new ArdourCanvas::SimpleRect (*group
);
3252 _step_edit_cursor
->property_y1() = 0;
3253 _step_edit_cursor
->property_y2() = midi_stream_view()->contents_height();
3254 _step_edit_cursor
->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3255 _step_edit_cursor
->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3258 move_step_edit_cursor (pos
);
3259 _step_edit_cursor
->show ();
3263 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos
)
3265 _step_edit_cursor_position
= pos
;
3267 if (_step_edit_cursor
) {
3268 double pixel
= trackview
.editor().frame_to_pixel (beats_to_frames (pos
));
3269 _step_edit_cursor
->property_x1() = pixel
;
3270 set_step_edit_cursor_width (_step_edit_cursor_width
);
3275 MidiRegionView::hide_step_edit_cursor ()
3277 if (_step_edit_cursor
) {
3278 _step_edit_cursor
->hide ();
3283 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats
)
3285 _step_edit_cursor_width
= beats
;
3287 if (_step_edit_cursor
) {
3288 _step_edit_cursor
->property_x2() = _step_edit_cursor
->property_x1() + trackview
.editor().frame_to_pixel (beats_to_frames (beats
));
3292 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3293 * @param buf Data that has been recorded.
3294 * @param w Source that this data will end up in.
3297 MidiRegionView::data_recorded (boost::shared_ptr
<MidiBuffer
> buf
, boost::weak_ptr
<MidiSource
> w
)
3299 if (!_active_notes
) {
3300 /* we aren't actively being recorded to */
3304 boost::shared_ptr
<MidiSource
> src
= w
.lock ();
3305 if (!src
|| src
!= midi_region()->midi_source()) {
3306 /* recorded data was not destined for our source */
3310 MidiTimeAxisView
* mtv
= dynamic_cast<MidiTimeAxisView
*> (&trackview
);
3311 BeatsFramesConverter
converter (trackview
.session()->tempo_map(), mtv
->midi_track()->get_capture_start_frame (0));
3313 framepos_t back
= max_framepos
;
3315 for (MidiBuffer::iterator i
= buf
->begin(); i
!= buf
->end(); ++i
) {
3316 Evoral::MIDIEvent
<MidiBuffer::TimeType
> const ev (*i
, false);
3317 assert (ev
.buffer ());
3319 Evoral::MusicalTime
const time_beats
= converter
.from (ev
.time () - converter
.origin_b ());
3321 if (ev
.type() == MIDI_CMD_NOTE_ON
) {
3323 boost::shared_ptr
<Evoral::Note
<Evoral::MusicalTime
> > note (
3324 new Evoral::Note
<Evoral::MusicalTime
> (ev
.channel(), time_beats
, 0, ev
.note(), ev
.velocity())
3327 add_note (note
, true);
3329 /* fix up our note range */
3330 if (ev
.note() < _current_range_min
) {
3331 midi_stream_view()->apply_note_range (ev
.note(), _current_range_max
, true);
3332 } else if (ev
.note() > _current_range_max
) {
3333 midi_stream_view()->apply_note_range (_current_range_min
, ev
.note(), true);
3336 } else if (ev
.type() == MIDI_CMD_NOTE_OFF
) {
3337 resolve_note (ev
.note (), time_beats
);
3343 midi_stream_view()->check_record_layers (region(), back
);
3347 MidiRegionView::trim_front_starting ()
3349 /* Reparent the note group to the region view's parent, so that it doesn't change
3350 when the region view is trimmed.
3352 _temporary_note_group
= new ArdourCanvas::Group (*group
->property_parent ());
3353 _temporary_note_group
->move (group
->property_x(), group
->property_y());
3354 _note_group
->reparent (*_temporary_note_group
);
3358 MidiRegionView::trim_front_ending ()
3360 _note_group
->reparent (*group
);
3361 delete _temporary_note_group
;
3362 _temporary_note_group
= 0;
3364 if (_region
->start() < 0) {
3365 /* Trim drag made start time -ve; fix this */
3366 midi_region()->fix_negative_start ();
3370 /** @return channel (counted from 0) to add an event to, based on the current setting
3371 * of the channel selector.
3374 MidiRegionView::get_channel_for_add () const
3376 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
3377 uint16_t const chn_mask
= mtv
->channel_selector().get_selected_channels();
3379 uint8_t channel
= 0;
3381 /* pick the highest selected channel, unless all channels are selected,
3382 which is interpreted to mean channel 1 (zero)
3385 for (uint16_t i
= 0; i
< 16; ++i
) {
3386 if (chn_mask
& (1<<i
)) {
3392 if (chn_cnt
== 16) {
3400 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange
* pc
)
3402 PatchChangeDialog
d (&_time_converter
, trackview
.session(), *pc
->patch (), Gtk::Stock::APPLY
);
3403 if (d
.run () != Gtk::RESPONSE_ACCEPT
) {
3407 change_patch_change (pc
->patch(), d
.patch ());