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 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1328 if ((note
= dynamic_cast<CanvasNote
*>(*i
)) != 0) {
1329 ghost
->add_note(note
);
1333 ghost
->set_height ();
1334 ghost
->set_duration (_region
->length() / samples_per_unit
);
1335 ghosts
.push_back (ghost
);
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());
1460 /** Update a canvas note's size from its model note.
1461 * @param ev Canvas note to update.
1462 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1465 MidiRegionView::update_note (CanvasNote
* ev
, bool update_ghost_regions
)
1467 boost::shared_ptr
<NoteType
> note
= ev
->note();
1469 const framepos_t note_start_frames
= beats_to_frames(note
->time());
1471 /* trim note display to not overlap the end of its region */
1472 const framepos_t note_end_frames
= min (beats_to_frames (note
->end_time()), _region
->start() + _region
->length());
1474 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1475 const double y1
= midi_stream_view()->note_to_y(note
->note());
1476 const double note_endpixel
= trackview
.editor().frame_to_pixel(note_end_frames
- _region
->start());
1478 ev
->property_x1() = x
;
1479 ev
->property_y1() = y1
;
1481 if (note
->length() > 0) {
1482 ev
->property_x2() = note_endpixel
;
1484 ev
->property_x2() = trackview
.editor().frame_to_pixel(_region
->length());
1487 ev
->property_y2() = y1
+ floor(midi_stream_view()->note_height());
1489 if (note
->length() == 0) {
1490 if (_active_notes
) {
1491 assert(note
->note() < 128);
1492 // If this note is already active there's a stuck note,
1493 // finish the old note rectangle
1494 if (_active_notes
[note
->note()]) {
1495 CanvasNote
* const old_rect
= _active_notes
[note
->note()];
1496 boost::shared_ptr
<NoteType
> old_note
= old_rect
->note();
1497 old_rect
->property_x2() = x
;
1498 old_rect
->property_outline_what() = (guint32
) 0xF;
1500 _active_notes
[note
->note()] = ev
;
1502 /* outline all but right edge */
1503 ev
->property_outline_what() = (guint32
) (0x1 & 0x4 & 0x8);
1505 /* outline all edges */
1506 ev
->property_outline_what() = (guint32
) 0xF;
1509 if (update_ghost_regions
) {
1510 for (std::vector
<GhostRegion
*>::iterator i
= ghosts
.begin(); i
!= ghosts
.end(); ++i
) {
1511 MidiGhostRegion
* gr
= dynamic_cast<MidiGhostRegion
*> (*i
);
1513 gr
->update_note (ev
);
1520 MidiRegionView::update_hit (CanvasHit
* ev
)
1522 boost::shared_ptr
<NoteType
> note
= ev
->note();
1524 const framepos_t note_start_frames
= beats_to_frames(note
->time());
1525 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1526 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1527 const double y
= midi_stream_view()->note_to_y(note
->note()) + ((diamond_size
-2) / 4.0);
1531 return diamond_size
;
1534 /** Add a MIDI note to the view (with length).
1536 * If in sustained mode, notes with length 0 will be considered active
1537 * notes, and resolve_note should be called when the corresponding note off
1538 * event arrives, to properly display the note.
1541 MidiRegionView::add_note(const boost::shared_ptr
<NoteType
> note
, bool visible
)
1543 CanvasNoteEvent
* event
= 0;
1545 assert(note
->time() >= 0);
1546 assert(midi_view()->note_mode() == Sustained
|| midi_view()->note_mode() == Percussive
);
1548 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1550 if (midi_view()->note_mode() == Sustained
) {
1552 CanvasNote
* ev_rect
= new CanvasNote(*this, *_note_group
, note
);
1554 update_note (ev_rect
);
1558 MidiGhostRegion
* gr
;
1560 for (std::vector
<GhostRegion
*>::iterator g
= ghosts
.begin(); g
!= ghosts
.end(); ++g
) {
1561 if ((gr
= dynamic_cast<MidiGhostRegion
*>(*g
)) != 0) {
1562 gr
->add_note(ev_rect
);
1566 } else if (midi_view()->note_mode() == Percussive
) {
1568 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1570 CanvasHit
* ev_diamond
= new CanvasHit(*this, *_note_group
, diamond_size
, note
);
1572 update_hit (ev_diamond
);
1581 if (_marked_for_selection
.find(note
) != _marked_for_selection
.end()) {
1582 note_selected(event
, true);
1585 if (_marked_for_velocity
.find(note
) != _marked_for_velocity
.end()) {
1586 event
->show_velocity();
1589 event
->on_channel_selection_change(_last_channel_selection
);
1590 _events
.push_back(event
);
1599 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
1600 MidiStreamView
* const view
= mtv
->midi_view();
1602 view
->update_note_range(note
->note());
1606 MidiRegionView::step_add_note (uint8_t channel
, uint8_t number
, uint8_t velocity
,
1607 Evoral::MusicalTime pos
, Evoral::MusicalTime len
)
1609 boost::shared_ptr
<NoteType
> new_note (new NoteType (channel
, pos
, len
, number
, velocity
));
1611 /* potentially extend region to hold new note */
1613 framepos_t end_frame
= _region
->position() + beats_to_frames (new_note
->end_time());
1614 framepos_t region_end
= _region
->position() + _region
->length() - 1;
1616 if (end_frame
> region_end
) {
1617 _region
->set_length (end_frame
- _region
->position(), this);
1620 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
1621 MidiStreamView
* const view
= mtv
->midi_view();
1623 view
->update_note_range(new_note
->note());
1625 _marked_for_selection
.clear ();
1628 start_note_diff_command (_("step add"));
1629 note_diff_add_note (new_note
, true, false);
1632 // last_step_edit_note = new_note;
1636 MidiRegionView::step_sustain (Evoral::MusicalTime beats
)
1638 change_note_lengths (false, false, beats
, false, true);
1642 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch
, const string
& displaytext
)
1644 assert (patch
->time() >= 0);
1646 const double x
= trackview
.editor().frame_to_pixel (beats_to_frames (patch
->time()));
1648 double const height
= midi_stream_view()->contents_height();
1650 boost::shared_ptr
<CanvasPatchChange
> patch_change
= boost::shared_ptr
<CanvasPatchChange
>(
1651 new CanvasPatchChange(*this, *_note_group
,
1656 _custom_device_mode
,
1660 // Show unless patch change is beyond the region bounds
1661 if (patch
->time() - _region
->start() >= _region
->length() || patch
->time() < _region
->start()) {
1662 patch_change
->hide();
1664 patch_change
->show();
1667 _patch_changes
.push_back (patch_change
);
1671 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time
, uint8_t channel
, MIDI::Name::PatchPrimaryKey
& key
)
1673 MidiModel::PatchChanges::iterator i
= _model
->patch_change_lower_bound (time
);
1674 while (i
!= _model
->patch_changes().end() && (*i
)->channel() != channel
) {
1678 if (i
!= _model
->patch_changes().end()) {
1679 key
.msb
= (*i
)->bank_msb ();
1680 key
.lsb
= (*i
)->bank_lsb ();
1681 key
.program_number
= (*i
)->program ();
1683 key
.msb
= key
.lsb
= key
.program_number
= 0;
1686 assert (key
.is_sane());
1691 MidiRegionView::change_patch_change (CanvasPatchChange
& pc
, const MIDI::Name::PatchPrimaryKey
& new_patch
)
1693 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("alter patch change"));
1695 if (pc
.patch()->program() != new_patch
.program_number
) {
1696 c
->change_program (pc
.patch (), new_patch
.program_number
);
1699 int const new_bank
= (new_patch
.msb
<< 7) | new_patch
.lsb
;
1700 if (pc
.patch()->bank() != new_bank
) {
1701 c
->change_bank (pc
.patch (), new_bank
);
1704 _model
->apply_command (*trackview
.session(), c
);
1706 _patch_changes
.clear ();
1707 display_patch_changes ();
1711 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change
, const Evoral::PatchChange
<Evoral::MusicalTime
> & new_change
)
1713 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("alter patch change"));
1715 if (old_change
->time() != new_change
.time()) {
1716 c
->change_time (old_change
, new_change
.time());
1719 if (old_change
->channel() != new_change
.channel()) {
1720 c
->change_channel (old_change
, new_change
.channel());
1723 if (old_change
->program() != new_change
.program()) {
1724 c
->change_program (old_change
, new_change
.program());
1727 if (old_change
->bank() != new_change
.bank()) {
1728 c
->change_bank (old_change
, new_change
.bank());
1731 _model
->apply_command (*trackview
.session(), c
);
1733 _patch_changes
.clear ();
1734 display_patch_changes ();
1737 /** Add a patch change to the region.
1738 * @param t Time in frames relative to region position
1739 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1740 * get_channel_for_add())
1743 MidiRegionView::add_patch_change (framecnt_t t
, Evoral::PatchChange
<Evoral::MusicalTime
> const & patch
)
1745 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("add patch change"));
1746 c
->add (MidiModel::PatchChangePtr (
1747 new Evoral::PatchChange
<Evoral::MusicalTime
> (
1748 frames_to_beats (t
+ midi_region()->start()), get_channel_for_add(), patch
.program(), patch
.bank()
1752 _model
->apply_command (*trackview
.session(), c
);
1754 _patch_changes
.clear ();
1755 display_patch_changes ();
1759 MidiRegionView::move_patch_change (CanvasPatchChange
& pc
, Evoral::MusicalTime t
)
1761 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("move patch change"));
1762 c
->change_time (pc
.patch (), t
);
1763 _model
->apply_command (*trackview
.session(), c
);
1765 _patch_changes
.clear ();
1766 display_patch_changes ();
1770 MidiRegionView::delete_patch_change (CanvasPatchChange
* pc
)
1772 MidiModel::PatchChangeDiffCommand
* c
= _model
->new_patch_change_diff_command (_("delete patch change"));
1773 c
->remove (pc
->patch ());
1774 _model
->apply_command (*trackview
.session(), c
);
1776 _patch_changes
.clear ();
1777 display_patch_changes ();
1781 MidiRegionView::previous_patch (CanvasPatchChange
& patch
)
1783 if (patch
.patch()->program() < 127) {
1784 MIDI::Name::PatchPrimaryKey key
;
1785 get_patch_key_at (patch
.patch()->time(), patch
.patch()->channel(), key
);
1786 key
.program_number
++;
1787 change_patch_change (patch
, key
);
1792 MidiRegionView::next_patch (CanvasPatchChange
& patch
)
1794 if (patch
.patch()->program() > 0) {
1795 MIDI::Name::PatchPrimaryKey key
;
1796 get_patch_key_at (patch
.patch()->time(), patch
.patch()->channel(), key
);
1797 key
.program_number
--;
1798 change_patch_change (patch
, key
);
1803 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent
* cne
)
1805 if (_selection
.empty()) {
1809 if (_selection
.erase (cne
) > 0) {
1810 cerr
<< "Erased a CNE from selection\n";
1815 MidiRegionView::delete_selection()
1817 if (_selection
.empty()) {
1821 start_note_diff_command (_("delete selection"));
1823 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1824 if ((*i
)->selected()) {
1825 _note_diff_command
->remove((*i
)->note());
1835 MidiRegionView::delete_note (boost::shared_ptr
<NoteType
> n
)
1837 start_note_diff_command (_("delete note"));
1838 _note_diff_command
->remove (n
);
1841 trackview
.editor().hide_verbose_canvas_cursor ();
1845 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent
* ev
)
1847 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1848 if ((*i
)->selected() && (*i
) != ev
) {
1849 (*i
)->set_selected(false);
1850 (*i
)->hide_velocity();
1858 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent
* ev
)
1860 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
1863 Selection::iterator tmp
= i
;
1866 (*i
)->set_selected (false);
1867 _selection
.erase (i
);
1876 /* don't bother with removing this regionview from the editor selection,
1877 since we're about to add another note, and thus put/keep this
1878 regionview in the editor selection.
1881 if (!ev
->selected()) {
1882 add_to_selection (ev
);
1887 MidiRegionView::select_all_notes ()
1891 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1892 add_to_selection (*i
);
1897 MidiRegionView::select_matching_notes (uint8_t notenum
, uint16_t channel_mask
, bool add
, bool extend
)
1899 uint8_t low_note
= 127;
1900 uint8_t high_note
= 0;
1901 MidiModel::Notes
& notes (_model
->notes());
1902 _optimization_iterator
= _events
.begin();
1908 if (extend
&& _selection
.empty()) {
1914 /* scan existing selection to get note range */
1916 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1917 if ((*i
)->note()->note() < low_note
) {
1918 low_note
= (*i
)->note()->note();
1920 if ((*i
)->note()->note() > high_note
) {
1921 high_note
= (*i
)->note()->note();
1925 low_note
= min (low_note
, notenum
);
1926 high_note
= max (high_note
, notenum
);
1929 no_sound_notes
= true;
1931 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1933 boost::shared_ptr
<NoteType
> note (*n
);
1934 CanvasNoteEvent
* cne
;
1935 bool select
= false;
1937 if (((1 << note
->channel()) & channel_mask
) != 0) {
1939 if ((note
->note() >= low_note
&& note
->note() <= high_note
)) {
1942 } else if (note
->note() == notenum
) {
1948 if ((cne
= find_canvas_note (note
)) != 0) {
1949 // extend is false because we've taken care of it,
1950 // since it extends by time range, not pitch.
1951 note_selected (cne
, add
, false);
1955 add
= true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1959 no_sound_notes
= false;
1963 MidiRegionView::toggle_matching_notes (uint8_t notenum
, uint16_t channel_mask
)
1965 MidiModel::Notes
& notes (_model
->notes());
1966 _optimization_iterator
= _events
.begin();
1968 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1970 boost::shared_ptr
<NoteType
> note (*n
);
1971 CanvasNoteEvent
* cne
;
1973 if (note
->note() == notenum
&& (((0x0001 << note
->channel()) & channel_mask
) != 0)) {
1974 if ((cne
= find_canvas_note (note
)) != 0) {
1975 if (cne
->selected()) {
1976 note_deselected (cne
);
1978 note_selected (cne
, true, false);
1986 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent
* ev
, bool add
, bool extend
)
1989 clear_selection_except(ev
);
1994 if (!ev
->selected()) {
1995 add_to_selection (ev
);
1999 /* find end of latest note selected, select all between that and the start of "ev" */
2001 Evoral::MusicalTime earliest
= Evoral::MaxMusicalTime
;
2002 Evoral::MusicalTime latest
= 0;
2004 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2005 if ((*i
)->note()->end_time() > latest
) {
2006 latest
= (*i
)->note()->end_time();
2008 if ((*i
)->note()->time() < earliest
) {
2009 earliest
= (*i
)->note()->time();
2013 if (ev
->note()->end_time() > latest
) {
2014 latest
= ev
->note()->end_time();
2017 if (ev
->note()->time() < earliest
) {
2018 earliest
= ev
->note()->time();
2021 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2023 /* find notes entirely within OR spanning the earliest..latest range */
2025 if (((*i
)->note()->time() >= earliest
&& (*i
)->note()->end_time() <= latest
) ||
2026 ((*i
)->note()->time() <= earliest
&& (*i
)->note()->end_time() >= latest
)) {
2027 add_to_selection (*i
);
2031 /* if events were guaranteed to be time sorted, we could do this.
2032 but as of sept 10th 2009, they no longer are.
2035 if ((*i
)->note()->time() > latest
) {
2044 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent
* ev
)
2046 remove_from_selection (ev
);
2050 MidiRegionView::update_drag_selection(double x1
, double x2
, double y1
, double y2
)
2060 // TODO: Make this faster by storing the last updated selection rect, and only
2061 // adjusting things that are in the area that appears/disappeared.
2062 // We probably need a tree to be able to find events in O(log(n)) time.
2064 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2066 /* check if any corner of the note is inside the rect
2069 1) this is computing "touched by", not "contained by" the rect.
2070 2) this does not require that events be sorted in time.
2073 const double ix1
= (*i
)->x1();
2074 const double ix2
= (*i
)->x2();
2075 const double iy1
= (*i
)->y1();
2076 const double iy2
= (*i
)->y2();
2078 if ((ix1
>= x1
&& ix1
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
2079 (ix1
>= x1
&& ix1
<= x2
&& iy2
>= y1
&& iy2
<= y2
) ||
2080 (ix2
>= x1
&& ix2
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
2081 (ix2
>= x1
&& ix2
<= x2
&& iy2
>= y1
&& iy2
<= y2
)) {
2084 if (!(*i
)->selected()) {
2085 add_to_selection (*i
);
2087 } else if ((*i
)->selected()) {
2088 // Not inside rectangle
2089 remove_from_selection (*i
);
2095 MidiRegionView::remove_from_selection (CanvasNoteEvent
* ev
)
2097 Selection::iterator i
= _selection
.find (ev
);
2099 if (i
!= _selection
.end()) {
2100 _selection
.erase (i
);
2103 ev
->set_selected (false);
2104 ev
->hide_velocity ();
2106 if (_selection
.empty()) {
2107 PublicEditor
& editor (trackview
.editor());
2108 editor
.get_selection().remove (this);
2113 MidiRegionView::add_to_selection (CanvasNoteEvent
* ev
)
2115 bool add_mrv_selection
= false;
2117 if (_selection
.empty()) {
2118 add_mrv_selection
= true;
2121 if (_selection
.insert (ev
).second
) {
2122 ev
->set_selected (true);
2123 play_midi_note ((ev
)->note());
2126 if (add_mrv_selection
) {
2127 PublicEditor
& editor (trackview
.editor());
2128 editor
.get_selection().add (this);
2133 MidiRegionView::move_selection(double dx
, double dy
, double cumulative_dy
)
2135 typedef vector
<boost::shared_ptr
<NoteType
> > PossibleChord
;
2136 PossibleChord to_play
;
2137 Evoral::MusicalTime earliest
= Evoral::MaxMusicalTime
;
2139 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2140 if ((*i
)->note()->time() < earliest
) {
2141 earliest
= (*i
)->note()->time();
2145 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2146 if (Evoral::musical_time_equal ((*i
)->note()->time(), earliest
)) {
2147 to_play
.push_back ((*i
)->note());
2149 (*i
)->move_event(dx
, dy
);
2152 if (dy
&& !_selection
.empty() && !no_sound_notes
&& trackview
.editor().sound_notes()) {
2154 if (to_play
.size() > 1) {
2156 PossibleChord shifted
;
2158 for (PossibleChord::iterator n
= to_play
.begin(); n
!= to_play
.end(); ++n
) {
2159 boost::shared_ptr
<NoteType
> moved_note (new NoteType (**n
));
2160 moved_note
->set_note (moved_note
->note() + cumulative_dy
);
2161 shifted
.push_back (moved_note
);
2164 play_midi_chord (shifted
);
2166 } else if (!to_play
.empty()) {
2168 boost::shared_ptr
<NoteType
> moved_note (new NoteType (*to_play
.front()));
2169 moved_note
->set_note (moved_note
->note() + cumulative_dy
);
2170 play_midi_note (moved_note
);
2176 MidiRegionView::note_dropped(CanvasNoteEvent
*, frameoffset_t dt
, int8_t dnote
)
2178 assert (!_selection
.empty());
2180 uint8_t lowest_note_in_selection
= 127;
2181 uint8_t highest_note_in_selection
= 0;
2182 uint8_t highest_note_difference
= 0;
2184 // find highest and lowest notes first
2186 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2187 uint8_t pitch
= (*i
)->note()->note();
2188 lowest_note_in_selection
= std::min(lowest_note_in_selection
, pitch
);
2189 highest_note_in_selection
= std::max(highest_note_in_selection
, pitch
);
2193 cerr << "dnote: " << (int) dnote << endl;
2194 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2195 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2196 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2197 << int(highest_note_in_selection) << endl;
2198 cerr << "selection size: " << _selection.size() << endl;
2199 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2202 // Make sure the note pitch does not exceed the MIDI standard range
2203 if (highest_note_in_selection
+ dnote
> 127) {
2204 highest_note_difference
= highest_note_in_selection
- 127;
2207 start_note_diff_command (_("move notes"));
2209 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end() ; ++i
) {
2211 Evoral::MusicalTime new_time
= frames_to_beats (beats_to_frames ((*i
)->note()->time()) + dt
);
2217 note_diff_add_change (*i
, MidiModel::NoteDiffCommand::StartTime
, new_time
);
2219 uint8_t original_pitch
= (*i
)->note()->note();
2220 uint8_t new_pitch
= original_pitch
+ dnote
- highest_note_difference
;
2222 // keep notes in standard midi range
2223 clamp_to_0_127(new_pitch
);
2225 // keep original pitch if note is dragged outside valid midi range
2226 if ((original_pitch
!= 0 && new_pitch
== 0)
2227 || (original_pitch
!= 127 && new_pitch
== 127)) {
2228 new_pitch
= original_pitch
;
2231 lowest_note_in_selection
= std::min(lowest_note_in_selection
, new_pitch
);
2232 highest_note_in_selection
= std::max(highest_note_in_selection
, new_pitch
);
2234 note_diff_add_change (*i
, MidiModel::NoteDiffCommand::NoteNumber
, new_pitch
);
2239 // care about notes being moved beyond the upper/lower bounds on the canvas
2240 if (lowest_note_in_selection
< midi_stream_view()->lowest_note() ||
2241 highest_note_in_selection
> midi_stream_view()->highest_note()) {
2242 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange
);
2247 MidiRegionView::snap_pixel_to_frame(double x
)
2249 PublicEditor
& editor
= trackview
.editor();
2250 // x is region relative, convert it to global absolute frames
2251 framepos_t frame
= editor
.pixel_to_frame(x
) + _region
->position();
2252 editor
.snap_to(frame
);
2253 return frame
- _region
->position(); // convert back to region relative
2257 MidiRegionView::snap_frame_to_frame(framepos_t x
)
2259 PublicEditor
& editor
= trackview
.editor();
2260 // x is region relative, convert it to global absolute frames
2261 framepos_t frame
= x
+ _region
->position();
2262 editor
.snap_to(frame
);
2263 return frame
- _region
->position(); // convert back to region relative
2267 MidiRegionView::snap_to_pixel(double x
)
2269 return (double) trackview
.editor().frame_to_pixel(snap_pixel_to_frame(x
));
2273 MidiRegionView::get_position_pixels()
2275 framepos_t region_frame
= get_position();
2276 return trackview
.editor().frame_to_pixel(region_frame
);
2280 MidiRegionView::get_end_position_pixels()
2282 framepos_t frame
= get_position() + get_duration ();
2283 return trackview
.editor().frame_to_pixel(frame
);
2287 MidiRegionView::beats_to_frames(double beats
) const
2289 return _time_converter
.to(beats
);
2293 MidiRegionView::frames_to_beats(framepos_t frames
) const
2295 return _time_converter
.from(frames
);
2299 MidiRegionView::begin_resizing (bool /*at_front*/)
2301 _resize_data
.clear();
2303 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2304 CanvasNote
*note
= dynamic_cast<CanvasNote
*> (*i
);
2306 // only insert CanvasNotes into the map
2308 NoteResizeData
*resize_data
= new NoteResizeData();
2309 resize_data
->canvas_note
= note
;
2311 // create a new SimpleRect from the note which will be the resize preview
2312 SimpleRect
*resize_rect
= new SimpleRect(
2313 *_note_group
, note
->x1(), note
->y1(), note
->x2(), note
->y2());
2315 // calculate the colors: get the color settings
2316 uint32_t fill_color
= UINT_RGBA_CHANGE_A(
2317 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get(),
2320 // make the resize preview notes more transparent and bright
2321 fill_color
= UINT_INTERPOLATE(fill_color
, 0xFFFFFF40, 0.5);
2323 // calculate color based on note velocity
2324 resize_rect
->property_fill_color_rgba() = UINT_INTERPOLATE(
2325 CanvasNoteEvent::meter_style_fill_color(note
->note()->velocity(), note
->selected()),
2329 resize_rect
->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2330 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get());
2332 resize_data
->resize_rect
= resize_rect
;
2333 _resize_data
.push_back(resize_data
);
2338 /** Update resizing notes while user drags.
2339 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2340 * @param at_front which end of the note (true == note on, false == note off)
2341 * @param delta_x change in mouse position since the start of the drag
2342 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2343 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2344 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2345 * as the \a primary note.
2348 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent
* primary
, bool at_front
, double delta_x
, bool relative
)
2350 bool cursor_set
= false;
2352 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2353 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2354 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2359 current_x
= canvas_note
->x1() + delta_x
;
2361 current_x
= primary
->x1() + delta_x
;
2365 current_x
= canvas_note
->x2() + delta_x
;
2367 current_x
= primary
->x2() + delta_x
;
2372 resize_rect
->property_x1() = snap_to_pixel(current_x
);
2373 resize_rect
->property_x2() = canvas_note
->x2();
2375 resize_rect
->property_x2() = snap_to_pixel(current_x
);
2376 resize_rect
->property_x1() = canvas_note
->x1();
2382 beats
= snap_pixel_to_frame (current_x
);
2383 beats
= frames_to_beats (beats
);
2388 if (beats
< canvas_note
->note()->end_time()) {
2389 len
= canvas_note
->note()->time() - beats
;
2390 len
+= canvas_note
->note()->length();
2395 if (beats
>= canvas_note
->note()->time()) {
2396 len
= beats
- canvas_note
->note()->time();
2403 snprintf (buf
, sizeof (buf
), "%.3g beats", len
);
2404 trackview
.editor().show_verbose_canvas_cursor_with (buf
);
2413 /** Finish resizing notes when the user releases the mouse button.
2414 * Parameters the same as for \a update_resizing().
2417 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent
* primary
, bool at_front
, double delta_x
, bool relative
)
2419 start_note_diff_command (_("resize notes"));
2421 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2422 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2423 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2428 current_x
= canvas_note
->x1() + delta_x
;
2430 current_x
= primary
->x1() + delta_x
;
2434 current_x
= canvas_note
->x2() + delta_x
;
2436 current_x
= primary
->x2() + delta_x
;
2440 current_x
= snap_pixel_to_frame (current_x
);
2441 current_x
= frames_to_beats (current_x
);
2443 if (at_front
&& current_x
< canvas_note
->note()->end_time()) {
2444 note_diff_add_change (canvas_note
, MidiModel::NoteDiffCommand::StartTime
, current_x
);
2446 double len
= canvas_note
->note()->time() - current_x
;
2447 len
+= canvas_note
->note()->length();
2450 /* XXX convert to beats */
2451 note_diff_add_change (canvas_note
, MidiModel::NoteDiffCommand::Length
, len
);
2456 double len
= current_x
- canvas_note
->note()->time();
2459 /* XXX convert to beats */
2460 note_diff_add_change (canvas_note
, MidiModel::NoteDiffCommand::Length
, len
);
2468 _resize_data
.clear();
2473 MidiRegionView::change_note_channel (CanvasNoteEvent
* event
, int8_t channel
)
2475 note_diff_add_change (event
, MidiModel::NoteDiffCommand::Channel
, (uint8_t) channel
);
2479 MidiRegionView::change_note_velocity(CanvasNoteEvent
* event
, int8_t velocity
, bool relative
)
2481 uint8_t new_velocity
;
2484 new_velocity
= event
->note()->velocity() + velocity
;
2485 clamp_to_0_127(new_velocity
);
2487 new_velocity
= velocity
;
2490 event
->set_selected (event
->selected()); // change color
2492 note_diff_add_change (event
, MidiModel::NoteDiffCommand::Velocity
, new_velocity
);
2496 MidiRegionView::change_note_note (CanvasNoteEvent
* event
, int8_t note
, bool relative
)
2501 new_note
= event
->note()->note() + note
;
2506 clamp_to_0_127 (new_note
);
2507 note_diff_add_change (event
, MidiModel::NoteDiffCommand::NoteNumber
, new_note
);
2511 MidiRegionView::trim_note (CanvasNoteEvent
* event
, Evoral::MusicalTime front_delta
, Evoral::MusicalTime end_delta
)
2513 bool change_start
= false;
2514 bool change_length
= false;
2515 Evoral::MusicalTime new_start
= 0;
2516 Evoral::MusicalTime new_length
= 0;
2518 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2520 front_delta: if positive - move the start of the note later in time (shortening it)
2521 if negative - move the start of the note earlier in time (lengthening it)
2523 end_delta: if positive - move the end of the note later in time (lengthening it)
2524 if negative - move the end of the note earlier in time (shortening it)
2528 if (front_delta
< 0) {
2530 if (event
->note()->time() < -front_delta
) {
2533 new_start
= event
->note()->time() + front_delta
; // moves earlier
2536 /* start moved toward zero, so move the end point out to where it used to be.
2537 Note that front_delta is negative, so this increases the length.
2540 new_length
= event
->note()->length() - front_delta
;
2541 change_start
= true;
2542 change_length
= true;
2546 Evoral::MusicalTime new_pos
= event
->note()->time() + front_delta
;
2548 if (new_pos
< event
->note()->end_time()) {
2549 new_start
= event
->note()->time() + front_delta
;
2550 /* start moved toward the end, so move the end point back to where it used to be */
2551 new_length
= event
->note()->length() - front_delta
;
2552 change_start
= true;
2553 change_length
= true;
2560 bool can_change
= true;
2561 if (end_delta
< 0) {
2562 if (event
->note()->length() < -end_delta
) {
2568 new_length
= event
->note()->length() + end_delta
;
2569 change_length
= true;
2574 note_diff_add_change (event
, MidiModel::NoteDiffCommand::StartTime
, new_start
);
2577 if (change_length
) {
2578 note_diff_add_change (event
, MidiModel::NoteDiffCommand::Length
, new_length
);
2583 MidiRegionView::change_note_time (CanvasNoteEvent
* event
, Evoral::MusicalTime delta
, bool relative
)
2585 Evoral::MusicalTime new_time
;
2589 if (event
->note()->time() < -delta
) {
2592 new_time
= event
->note()->time() + delta
;
2595 new_time
= event
->note()->time() + delta
;
2601 note_diff_add_change (event
, MidiModel::NoteDiffCommand::StartTime
, new_time
);
2605 MidiRegionView::change_note_length (CanvasNoteEvent
* event
, Evoral::MusicalTime t
)
2607 note_diff_add_change (event
, MidiModel::NoteDiffCommand::Length
, t
);
2611 MidiRegionView::change_velocities (bool up
, bool fine
, bool allow_smush
)
2615 if (_selection
.empty()) {
2630 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2631 if ((*i
)->note()->velocity() + delta
== 0 || (*i
)->note()->velocity() + delta
== 127) {
2637 start_note_diff_command (_("change velocities"));
2639 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end();) {
2640 Selection::iterator next
= i
;
2642 change_note_velocity (*i
, delta
, true);
2648 if (!_selection
.empty()) {
2650 snprintf (buf
, sizeof (buf
), "Vel %d",
2651 (int) (*_selection
.begin())->note()->velocity());
2652 trackview
.editor().show_verbose_canvas_cursor_with (buf
, 10, 10);
2658 MidiRegionView::transpose (bool up
, bool fine
, bool allow_smush
)
2660 if (_selection
.empty()) {
2677 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2679 if ((int8_t) (*i
)->note()->note() + delta
<= 0) {
2683 if ((int8_t) (*i
)->note()->note() + delta
> 127) {
2690 start_note_diff_command (_("transpose"));
2692 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2693 Selection::iterator next
= i
;
2695 change_note_note (*i
, delta
, true);
2703 MidiRegionView::change_note_lengths (bool fine
, bool shorter
, Evoral::MusicalTime delta
, bool start
, bool end
)
2709 /* grab the current grid distance */
2711 delta
= trackview
.editor().get_grid_type_as_beats (success
, _region
->position());
2713 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2714 cerr
<< "Grid type not available as beats - TO BE FIXED\n";
2724 start_note_diff_command (_("change note lengths"));
2726 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2727 Selection::iterator next
= i
;
2730 /* note the negation of the delta for start */
2732 trim_note (*i
, (start
? -delta
: 0), (end
? delta
: 0));
2741 MidiRegionView::nudge_notes (bool forward
)
2743 if (_selection
.empty()) {
2747 /* pick a note as the point along the timeline to get the nudge distance.
2748 its not necessarily the earliest note, so we may want to pull the notes out
2749 into a vector and sort before using the first one.
2752 framepos_t ref_point
= _region
->position() + beats_to_frames ((*(_selection
.begin()))->note()->time());
2754 framepos_t distance
;
2756 if (trackview
.editor().snap_mode() == Editing::SnapOff
) {
2758 /* grid is off - use nudge distance */
2760 distance
= trackview
.editor().get_nudge_distance (ref_point
, unused
);
2766 framepos_t next_pos
= ref_point
;
2769 if (max_framepos
- 1 < next_pos
) {
2773 if (next_pos
== 0) {
2779 trackview
.editor().snap_to (next_pos
, (forward
? 1 : -1), false);
2780 distance
= ref_point
- next_pos
;
2783 if (distance
== 0) {
2787 Evoral::MusicalTime delta
= frames_to_beats (fabs (distance
));
2793 start_note_diff_command (_("nudge"));
2795 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2796 Selection::iterator next
= i
;
2798 change_note_time (*i
, delta
, true);
2806 MidiRegionView::change_channel(uint8_t channel
)
2808 start_note_diff_command(_("change channel"));
2809 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2810 note_diff_add_change (*i
, MidiModel::NoteDiffCommand::Channel
, channel
);
2818 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent
* ev
)
2820 Editor
* editor
= dynamic_cast<Editor
*>(&trackview
.editor());
2822 pre_enter_cursor
= editor
->get_canvas_cursor ();
2824 if (_mouse_state
== SelectTouchDragging
) {
2825 note_selected (ev
, true);
2828 show_verbose_canvas_cursor (ev
->note ());
2832 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent
*)
2834 Editor
* editor
= dynamic_cast<Editor
*>(&trackview
.editor());
2836 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2837 (*i
)->hide_velocity ();
2840 editor
->hide_verbose_canvas_cursor ();
2842 if (pre_enter_cursor
) {
2843 editor
->set_canvas_cursor (pre_enter_cursor
);
2844 pre_enter_cursor
= 0;
2849 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange
* ev
)
2852 s
<< ((int) ev
->patch()->program() + 1) << ":" << (ev
->patch()->bank() + 1);
2853 trackview
.editor().show_verbose_canvas_cursor_with (s
.str().c_str(), 10, 20);
2857 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange
*)
2859 trackview
.editor().hide_verbose_canvas_cursor ();
2863 MidiRegionView::note_mouse_position (float x_fraction
, float /*y_fraction*/, bool can_set_cursor
)
2865 Editor
* editor
= dynamic_cast<Editor
*>(&trackview
.editor());
2867 if (x_fraction
> 0.0 && x_fraction
< 0.25) {
2868 editor
->set_canvas_cursor (editor
->cursors()->left_side_trim
);
2869 } else if (x_fraction
>= 0.75 && x_fraction
< 1.0) {
2870 editor
->set_canvas_cursor (editor
->cursors()->right_side_trim
);
2872 if (pre_enter_cursor
&& can_set_cursor
) {
2873 editor
->set_canvas_cursor (pre_enter_cursor
);
2879 MidiRegionView::set_frame_color()
2886 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase
.get();
2887 } else if (high_enough_for_name
) {
2888 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase
.get();
2890 frame
->property_fill_color_rgba() = fill_color
;
2895 MidiRegionView::midi_channel_mode_changed(ChannelMode mode
, uint16_t mask
)
2899 case FilterChannels
:
2900 _force_channel
= -1;
2903 _force_channel
= mask
;
2904 mask
= 0xFFFF; // Show all notes as active (below)
2907 // Update notes for selection
2908 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2909 (*i
)->on_channel_selection_change(mask
);
2912 _last_channel_selection
= mask
;
2914 _patch_changes
.clear ();
2915 display_patch_changes ();
2919 MidiRegionView::midi_patch_settings_changed(std::string model
, std::string custom_device_mode
)
2921 _model_name
= model
;
2922 _custom_device_mode
= custom_device_mode
;
2927 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op
)
2929 if (_selection
.empty()) {
2933 PublicEditor
& editor (trackview
.editor());
2938 editor
.get_cut_buffer().add (selection_as_cut_buffer());
2946 start_note_diff_command();
2948 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2954 note_diff_remove_note (*i
);
2964 MidiRegionView::selection_as_cut_buffer () const
2968 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2969 NoteType
* n
= (*i
)->note().get();
2970 notes
.insert (boost::shared_ptr
<NoteType
> (new NoteType (*n
)));
2973 MidiCutBuffer
* cb
= new MidiCutBuffer (trackview
.session());
2980 MidiRegionView::paste (framepos_t pos
, float times
, const MidiCutBuffer
& mcb
)
2986 DEBUG_TRACE (DEBUG::CutNPaste
, string_compose ("MIDI paste @ %1 times %2\n", pos
, times
));
2988 start_note_diff_command (_("paste"));
2990 Evoral::MusicalTime beat_delta
;
2991 Evoral::MusicalTime paste_pos_beats
;
2992 Evoral::MusicalTime duration
;
2993 Evoral::MusicalTime end_point
= 0;
2995 duration
= (*mcb
.notes().rbegin())->end_time() - (*mcb
.notes().begin())->time();
2996 paste_pos_beats
= frames_to_beats (pos
- _region
->position());
2997 beat_delta
= (*mcb
.notes().begin())->time() - paste_pos_beats
;
2998 paste_pos_beats
= 0;
3000 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",
3001 (*mcb
.notes().begin())->time(),
3002 (*mcb
.notes().rbegin())->end_time(),
3003 duration
, pos
, _region
->position(),
3004 paste_pos_beats
, beat_delta
));
3008 for (int n
= 0; n
< (int) times
; ++n
) {
3010 for (Notes::const_iterator i
= mcb
.notes().begin(); i
!= mcb
.notes().end(); ++i
) {
3012 boost::shared_ptr
<NoteType
> copied_note (new NoteType (*((*i
).get())));
3013 copied_note
->set_time (paste_pos_beats
+ copied_note
->time() - beat_delta
);
3015 /* make all newly added notes selected */
3017 note_diff_add_note (copied_note
, true);
3018 end_point
= copied_note
->end_time();
3021 paste_pos_beats
+= duration
;
3024 /* if we pasted past the current end of the region, extend the region */
3026 framepos_t end_frame
= _region
->position() + beats_to_frames (end_point
);
3027 framepos_t region_end
= _region
->position() + _region
->length() - 1;
3029 if (end_frame
> region_end
) {
3031 DEBUG_TRACE (DEBUG::CutNPaste
, string_compose ("Paste extended region from %1 to %2\n", region_end
, end_frame
));
3033 trackview
.session()->begin_reversible_command (_("paste"));
3035 _region
->clear_changes ();
3036 _region
->set_length (end_frame
, this);
3037 trackview
.session()->add_command (new StatefulDiffCommand (_region
));
3043 struct EventNoteTimeEarlyFirstComparator
{
3044 bool operator() (CanvasNoteEvent
* a
, CanvasNoteEvent
* b
) {
3045 return a
->note()->time() < b
->note()->time();
3050 MidiRegionView::time_sort_events ()
3052 if (!_sort_needed
) {
3056 EventNoteTimeEarlyFirstComparator cmp
;
3059 _sort_needed
= false;
3063 MidiRegionView::goto_next_note ()
3065 // framepos_t pos = -1;
3066 bool use_next
= false;
3068 if (_events
.back()->selected()) {
3072 time_sort_events ();
3074 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
3075 if ((*i
)->selected()) {
3078 } else if (use_next
) {
3080 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3085 /* use the first one */
3087 unique_select (_events
.front());
3092 MidiRegionView::goto_previous_note ()
3094 // framepos_t pos = -1;
3095 bool use_next
= false;
3097 if (_events
.front()->selected()) {
3101 time_sort_events ();
3103 for (Events::reverse_iterator i
= _events
.rbegin(); i
!= _events
.rend(); ++i
) {
3104 if ((*i
)->selected()) {
3107 } else if (use_next
) {
3109 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
3114 /* use the last one */
3116 unique_select (*(_events
.rbegin()));
3120 MidiRegionView::selection_as_notelist (Notes
& selected
, bool allow_all_if_none_selected
)
3122 bool had_selected
= false;
3124 time_sort_events ();
3126 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
3127 if ((*i
)->selected()) {
3128 selected
.insert ((*i
)->note());
3129 had_selected
= true;
3133 if (allow_all_if_none_selected
&& !had_selected
) {
3134 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
3135 selected
.insert ((*i
)->note());
3141 MidiRegionView::update_ghost_note (double x
, double y
)
3146 _note_group
->w2i (x
, y
);
3147 framepos_t f
= trackview
.editor().pixel_to_frame (x
) + _region
->position ();
3148 trackview
.editor().snap_to (f
);
3149 f
-= _region
->position ();
3152 Evoral::MusicalTime beats
= trackview
.editor().get_grid_type_as_beats (success
, f
);
3158 double length
= frames_to_beats (snap_frame_to_frame (f
+ beats_to_frames (beats
)) - f
);
3160 _ghost_note
->note()->set_time (frames_to_beats (f
+ _region
->start()));
3161 _ghost_note
->note()->set_length (length
);
3162 _ghost_note
->note()->set_note (midi_stream_view()->y_to_note (y
));
3164 /* the ghost note does not appear in ghost regions, so pass false in here */
3165 update_note (_ghost_note
, false);
3167 show_verbose_canvas_cursor (_ghost_note
->note ());
3171 MidiRegionView::create_ghost_note (double x
, double y
)
3176 boost::shared_ptr
<NoteType
> g (new NoteType
);
3177 _ghost_note
= new NoEventCanvasNote (*this, *_note_group
, g
);
3178 update_ghost_note (x
, y
);
3179 _ghost_note
->show ();
3184 show_verbose_canvas_cursor (_ghost_note
->note ());
3188 MidiRegionView::snap_changed ()
3194 create_ghost_note (_last_ghost_x
, _last_ghost_y
);
3198 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr
<NoteType
> n
) const
3201 snprintf (buf
, sizeof (buf
), "%s (%d)\nVel %d",
3202 Evoral::midi_note_name (n
->note()).c_str(),
3204 (int) n
->velocity());
3205 trackview
.editor().show_verbose_canvas_cursor_with (buf
, 10, 20);
3209 MidiRegionView::drop_down_keys ()
3211 _mouse_state
= None
;
3215 MidiRegionView::maybe_select_by_position (GdkEventButton
* ev
, double /*x*/, double y
)
3217 double note
= midi_stream_view()->y_to_note(y
);
3219 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
3221 uint16_t chn_mask
= mtv
->channel_selector().get_selected_channels();
3223 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::TertiaryModifier
)) {
3224 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchGreaterThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
3225 } else if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
3226 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchLessThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
3231 bool add_mrv_selection
= false;
3233 if (_selection
.empty()) {
3234 add_mrv_selection
= true;
3237 for (Events::iterator i
= e
.begin(); i
!= e
.end(); ++i
) {
3238 if (_selection
.insert (*i
).second
) {
3239 (*i
)->set_selected (true);
3243 if (add_mrv_selection
) {
3244 PublicEditor
& editor (trackview
.editor());
3245 editor
.get_selection().add (this);
3250 MidiRegionView::color_handler ()
3252 RegionView::color_handler ();
3254 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
3255 (*i
)->set_selected ((*i
)->selected()); // will change color
3258 /* XXX probably more to do here */
3262 MidiRegionView::enable_display (bool yn
)
3264 RegionView::enable_display (yn
);
3271 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos
)
3273 if (_step_edit_cursor
== 0) {
3274 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
3276 _step_edit_cursor
= new ArdourCanvas::SimpleRect (*group
);
3277 _step_edit_cursor
->property_y1() = 0;
3278 _step_edit_cursor
->property_y2() = midi_stream_view()->contents_height();
3279 _step_edit_cursor
->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3280 _step_edit_cursor
->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3283 move_step_edit_cursor (pos
);
3284 _step_edit_cursor
->show ();
3288 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos
)
3290 _step_edit_cursor_position
= pos
;
3292 if (_step_edit_cursor
) {
3293 double pixel
= trackview
.editor().frame_to_pixel (beats_to_frames (pos
));
3294 _step_edit_cursor
->property_x1() = pixel
;
3295 set_step_edit_cursor_width (_step_edit_cursor_width
);
3300 MidiRegionView::hide_step_edit_cursor ()
3302 if (_step_edit_cursor
) {
3303 _step_edit_cursor
->hide ();
3308 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats
)
3310 _step_edit_cursor_width
= beats
;
3312 if (_step_edit_cursor
) {
3313 _step_edit_cursor
->property_x2() = _step_edit_cursor
->property_x1() + trackview
.editor().frame_to_pixel (beats_to_frames (beats
));
3317 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3318 * @param buf Data that has been recorded.
3319 * @param w Source that this data will end up in.
3322 MidiRegionView::data_recorded (boost::shared_ptr
<MidiBuffer
> buf
, boost::weak_ptr
<MidiSource
> w
)
3324 if (!_active_notes
) {
3325 /* we aren't actively being recorded to */
3329 boost::shared_ptr
<MidiSource
> src
= w
.lock ();
3330 if (!src
|| src
!= midi_region()->midi_source()) {
3331 /* recorded data was not destined for our source */
3335 MidiTimeAxisView
* mtv
= dynamic_cast<MidiTimeAxisView
*> (&trackview
);
3336 BeatsFramesConverter
converter (trackview
.session()->tempo_map(), mtv
->midi_track()->get_capture_start_frame (0));
3338 framepos_t back
= max_framepos
;
3340 for (MidiBuffer::iterator i
= buf
->begin(); i
!= buf
->end(); ++i
) {
3341 Evoral::MIDIEvent
<MidiBuffer::TimeType
> const ev (*i
, false);
3342 assert (ev
.buffer ());
3344 Evoral::MusicalTime
const time_beats
= converter
.from (ev
.time () - converter
.origin_b ());
3346 if (ev
.type() == MIDI_CMD_NOTE_ON
) {
3348 boost::shared_ptr
<Evoral::Note
<Evoral::MusicalTime
> > note (
3349 new Evoral::Note
<Evoral::MusicalTime
> (ev
.channel(), time_beats
, 0, ev
.note(), ev
.velocity())
3352 add_note (note
, true);
3354 /* fix up our note range */
3355 if (ev
.note() < _current_range_min
) {
3356 midi_stream_view()->apply_note_range (ev
.note(), _current_range_max
, true);
3357 } else if (ev
.note() > _current_range_max
) {
3358 midi_stream_view()->apply_note_range (_current_range_min
, ev
.note(), true);
3361 } else if (ev
.type() == MIDI_CMD_NOTE_OFF
) {
3362 resolve_note (ev
.note (), time_beats
);
3368 midi_stream_view()->check_record_layers (region(), back
);
3372 MidiRegionView::trim_front_starting ()
3374 /* Reparent the note group to the region view's parent, so that it doesn't change
3375 when the region view is trimmed.
3377 _temporary_note_group
= new ArdourCanvas::Group (*group
->property_parent ());
3378 _temporary_note_group
->move (group
->property_x(), group
->property_y());
3379 _note_group
->reparent (*_temporary_note_group
);
3383 MidiRegionView::trim_front_ending ()
3385 _note_group
->reparent (*group
);
3386 delete _temporary_note_group
;
3387 _temporary_note_group
= 0;
3389 if (_region
->start() < 0) {
3390 /* Trim drag made start time -ve; fix this */
3391 midi_region()->fix_negative_start ();
3395 /** @return channel (counted from 0) to add an event to, based on the current setting
3396 * of the channel selector.
3399 MidiRegionView::get_channel_for_add () const
3401 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
3402 uint16_t const chn_mask
= mtv
->channel_selector().get_selected_channels();
3404 uint8_t channel
= 0;
3406 /* pick the highest selected channel, unless all channels are selected,
3407 which is interpreted to mean channel 1 (zero)
3410 for (uint16_t i
= 0; i
< 16; ++i
) {
3411 if (chn_mask
& (1<<i
)) {
3417 if (chn_cnt
== 16) {
3425 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange
* pc
)
3427 PatchChangeDialog
d (&_time_converter
, trackview
.session(), *pc
->patch (), Gtk::Stock::APPLY
);
3428 if (d
.run () != Gtk::RESPONSE_ACCEPT
) {
3432 change_patch_change (pc
->patch(), d
.patch ());