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-program-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_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"
73 using namespace ARDOUR
;
75 using namespace Editing
;
76 using namespace ArdourCanvas
;
77 using Gtkmm2ext::Keyboard
;
79 MidiRegionView::MidiRegionView (ArdourCanvas::Group
*parent
, RouteTimeAxisView
&tv
,
80 boost::shared_ptr
<MidiRegion
> r
, double spu
, Gdk::Color
const & basic_color
)
81 : RegionView (parent
, tv
, r
, spu
, basic_color
)
83 , _last_channel_selection(0xFFFF)
84 , _current_range_min(0)
85 , _current_range_max(0)
86 , _model_name(string())
87 , _custom_device_mode(string())
89 , _note_group(new ArdourCanvas::Group(*parent
))
93 , _step_edit_cursor (0)
94 , _step_edit_cursor_width (1.0)
95 , _step_edit_cursor_position (0.0)
99 , _optimization_iterator (_events
.end())
101 , no_sound_notes (false)
102 , pre_enter_cursor (0)
104 _note_group
->raise_to_top();
105 PublicEditor::DropDownKeys
.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys
));
107 connect_to_diskstream ();
110 MidiRegionView::MidiRegionView (ArdourCanvas::Group
*parent
, RouteTimeAxisView
&tv
,
111 boost::shared_ptr
<MidiRegion
> r
, double spu
, Gdk::Color
& basic_color
,
112 TimeAxisViewItem::Visibility visibility
)
113 : RegionView (parent
, tv
, r
, spu
, basic_color
, false, visibility
)
115 , _last_channel_selection(0xFFFF)
116 , _model_name(string())
117 , _custom_device_mode(string())
119 , _note_group(new ArdourCanvas::Group(*parent
))
123 , _step_edit_cursor (0)
124 , _step_edit_cursor_width (1.0)
125 , _step_edit_cursor_position (0.0)
128 , _sort_needed (true)
129 , _optimization_iterator (_events
.end())
131 , no_sound_notes (false)
133 _note_group
->raise_to_top();
134 PublicEditor::DropDownKeys
.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys
));
136 connect_to_diskstream ();
139 MidiRegionView::MidiRegionView (const MidiRegionView
& other
)
140 : sigc::trackable(other
)
143 , _last_channel_selection(0xFFFF)
144 , _model_name(string())
145 , _custom_device_mode(string())
147 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
151 , _step_edit_cursor (0)
152 , _step_edit_cursor_width (1.0)
153 , _step_edit_cursor_position (0.0)
156 , _sort_needed (true)
157 , _optimization_iterator (_events
.end())
159 , no_sound_notes (false)
164 UINT_TO_RGBA (other
.fill_color
, &r
, &g
, &b
, &a
);
165 c
.set_rgb_p (r
/255.0, g
/255.0, b
/255.0);
170 MidiRegionView::MidiRegionView (const MidiRegionView
& other
, boost::shared_ptr
<MidiRegion
> region
)
171 : RegionView (other
, boost::shared_ptr
<Region
> (region
))
173 , _last_channel_selection(0xFFFF)
174 , _model_name(string())
175 , _custom_device_mode(string())
177 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
181 , _step_edit_cursor (0)
182 , _step_edit_cursor_width (1.0)
183 , _step_edit_cursor_position (0.0)
186 , _sort_needed (true)
187 , _optimization_iterator (_events
.end())
189 , no_sound_notes (false)
194 UINT_TO_RGBA (other
.fill_color
, &r
, &g
, &b
, &a
);
195 c
.set_rgb_p (r
/255.0, g
/255.0, b
/255.0);
201 MidiRegionView::init (Gdk::Color
const & basic_color
, bool wfd
)
203 PublicEditor::DropDownKeys
.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys
));
205 CanvasNoteEvent::CanvasNoteEventDeleted
.connect (note_delete_connection
, MISSING_INVALIDATOR
,
206 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection
, this, _1
),
210 midi_region()->midi_source(0)->load_model();
213 _model
= midi_region()->midi_source(0)->model();
214 _enable_display
= false;
216 RegionView::init (basic_color
, false);
218 compute_colors (basic_color
);
220 set_height (trackview
.current_height());
223 region_sync_changed ();
224 region_resized (ARDOUR::bounds_change
);
227 reset_width_dependent_items (_pixel_width
);
231 _enable_display
= true;
234 display_model (_model
);
238 group
->raise_to_top();
239 group
->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event
), false);
241 midi_view()->signal_channel_mode_changed().connect(
242 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed
));
244 midi_view()->signal_midi_patch_settings_changed().connect(
245 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed
));
247 trackview
.editor().SnapChanged
.connect (snap_changed_connection
, invalidator (*this), ui_bind (&MidiRegionView::snap_changed
, this), gui_context ());
249 connect_to_diskstream ();
253 MidiRegionView::connect_to_diskstream ()
255 midi_view()->midi_track()->DataRecorded
.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::data_recorded
, this, _1
, _2
), gui_context ());
259 MidiRegionView::canvas_event(GdkEvent
* ev
)
261 if (!trackview
.editor().internal_editing()) {
265 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
266 to its items, which means that ev->type == GDK_SCROLL will never be seen
271 return scroll (&ev
->scroll
);
274 return key_press (&ev
->key
);
276 case GDK_KEY_RELEASE
:
277 return key_release (&ev
->key
);
279 case GDK_BUTTON_PRESS
:
280 return button_press (&ev
->button
);
282 case GDK_2BUTTON_PRESS
:
285 case GDK_BUTTON_RELEASE
:
286 return button_release (&ev
->button
);
288 case GDK_ENTER_NOTIFY
:
289 return enter_notify (&ev
->crossing
);
291 case GDK_LEAVE_NOTIFY
:
292 return leave_notify (&ev
->crossing
);
294 case GDK_MOTION_NOTIFY
:
295 return motion (&ev
->motion
);
305 MidiRegionView::enter_notify (GdkEventCrossing
* ev
)
307 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
309 Keyboard::magic_widget_grab_focus();
312 if (trackview
.editor().current_mouse_mode() == MouseRange
) {
313 create_ghost_note (ev
->x
, ev
->y
);
320 MidiRegionView::leave_notify (GdkEventCrossing
* ev
)
322 trackview
.editor().hide_verbose_canvas_cursor ();
329 MidiRegionView::button_press (GdkEventButton
* ev
)
333 group
->w2i (_last_x
, _last_y
);
335 if (_mouse_state
!= SelectTouchDragging
&& ev
->button
== 1) {
336 _pressed_button
= ev
->button
;
337 _mouse_state
= Pressed
;
341 _pressed_button
= ev
->button
;
347 MidiRegionView::button_release (GdkEventButton
* ev
)
349 double event_x
, event_y
;
350 framepos_t event_frame
= 0;
354 group
->w2i(event_x
, event_y
);
355 group
->ungrab(ev
->time
);
356 event_frame
= trackview
.editor().pixel_to_frame(event_x
);
358 if (ev
->button
== 3) {
360 } else if (_pressed_button
!= 1) {
364 switch (_mouse_state
) {
365 case Pressed
: // Clicked
366 switch (trackview
.editor().current_mouse_mode()) {
370 maybe_select_by_position (ev
, event_x
, event_y
);
376 Evoral::MusicalTime beats
= trackview
.editor().get_grid_type_as_beats (success
, trackview
.editor().pixel_to_frame (event_x
));
380 create_note_at (event_x
, event_y
, beats
, true);
388 case SelectRectDragging
: // Select drag done
394 case AddDragging
: // Add drag done
396 if (_drag_rect
->property_x2() > _drag_rect
->property_x1() + 2) {
397 const double x
= _drag_rect
->property_x1();
398 const double length
= trackview
.editor().pixel_to_frame
399 (_drag_rect
->property_x2() - _drag_rect
->property_x1());
401 create_note_at (x
, _drag_rect
->property_y1(), frames_to_beats(length
), false);
407 create_ghost_note (ev
->x
, ev
->y
);
417 MidiRegionView::motion (GdkEventMotion
* ev
)
419 double event_x
, event_y
;
420 framepos_t event_frame
= 0;
424 group
->w2i(event_x
, event_y
);
426 // convert event_x to global frame
427 event_frame
= trackview
.editor().pixel_to_frame(event_x
) + _region
->position();
428 trackview
.editor().snap_to(event_frame
);
429 // convert event_frame back to local coordinates relative to position
430 event_frame
-= _region
->position();
433 update_ghost_note (ev
->x
, ev
->y
);
436 /* any motion immediately hides velocity text that may have been visible */
438 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
439 (*i
)->hide_velocity ();
442 switch (_mouse_state
) {
443 case Pressed
: // Maybe start a drag, if we've moved a bit
445 if (fabs (event_x
- _last_x
) < 1 && fabs (event_y
- _last_y
) < 1) {
446 /* no appreciable movement since the button was pressed */
451 if (_pressed_button
== 1 && trackview
.editor().current_mouse_mode() == MouseObject
) {
452 group
->grab(GDK_POINTER_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
,
453 Gdk::Cursor(Gdk::FLEUR
), ev
->time
);
456 _drag_start_x
= event_x
;
457 _drag_start_y
= event_y
;
459 _drag_rect
= new ArdourCanvas::SimpleRect(*group
);
460 _drag_rect
->property_x1() = event_x
;
461 _drag_rect
->property_y1() = event_y
;
462 _drag_rect
->property_x2() = event_x
;
463 _drag_rect
->property_y2() = event_y
;
464 _drag_rect
->property_outline_what() = 0xFF;
465 _drag_rect
->property_outline_color_rgba()
466 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline
.get();
467 _drag_rect
->property_fill_color_rgba()
468 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill
.get();
470 _mouse_state
= SelectRectDragging
;
473 // Add note drag start
474 } else if (trackview
.editor().internal_editing()) {
479 group
->grab(GDK_POINTER_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
,
480 Gdk::Cursor(Gdk::FLEUR
), ev
->time
);
483 _drag_start_x
= event_x
;
484 _drag_start_y
= event_y
;
486 _drag_rect
= new ArdourCanvas::SimpleRect(*group
);
487 _drag_rect
->property_x1() = trackview
.editor().frame_to_pixel(event_frame
);
489 _drag_rect
->property_y1() = midi_stream_view()->note_to_y(
490 midi_stream_view()->y_to_note(event_y
));
491 _drag_rect
->property_x2() = trackview
.editor().frame_to_pixel(event_frame
);
492 _drag_rect
->property_y2() = _drag_rect
->property_y1()
493 + floor(midi_stream_view()->note_height());
494 _drag_rect
->property_outline_what() = 0xFF;
495 _drag_rect
->property_outline_color_rgba() = 0xFFFFFF99;
496 _drag_rect
->property_fill_color_rgba() = 0xFFFFFF66;
498 _mouse_state
= AddDragging
;
504 case SelectRectDragging
: // Select drag motion
505 case AddDragging
: // Add note drag motion
509 GdkModifierType state
;
510 gdk_window_get_pointer(ev
->window
, &t_x
, &t_y
, &state
);
515 if (_mouse_state
== AddDragging
)
516 event_x
= trackview
.editor().frame_to_pixel(event_frame
);
519 if (event_x
> _drag_start_x
)
520 _drag_rect
->property_x2() = event_x
;
522 _drag_rect
->property_x1() = event_x
;
525 if (_drag_rect
&& _mouse_state
== SelectRectDragging
) {
526 if (event_y
> _drag_start_y
)
527 _drag_rect
->property_y2() = event_y
;
529 _drag_rect
->property_y1() = event_y
;
531 update_drag_selection(_drag_start_x
, event_x
, _drag_start_y
, event_y
);
537 case SelectTouchDragging
:
549 MidiRegionView::scroll (GdkEventScroll
* ev
)
551 if (_selection
.empty()) {
555 trackview
.editor().hide_verbose_canvas_cursor ();
557 bool fine
= !Keyboard::modifier_state_equals (ev
->state
, Keyboard::SecondaryModifier
);
559 if (ev
->direction
== GDK_SCROLL_UP
) {
560 change_velocities (true, fine
, false);
561 } else if (ev
->direction
== GDK_SCROLL_DOWN
) {
562 change_velocities (false, fine
, false);
568 MidiRegionView::key_press (GdkEventKey
* ev
)
570 /* since GTK bindings are generally activated on press, and since
571 detectable auto-repeat is the name of the game and only sends
572 repeated presses, carry out key actions at key press, not release.
575 if (ev
->keyval
== GDK_Alt_L
|| ev
->keyval
== GDK_Alt_R
){
576 _mouse_state
= SelectTouchDragging
;
579 } else if (ev
->keyval
== GDK_Escape
) {
583 } else if (ev
->keyval
== GDK_comma
|| ev
->keyval
== GDK_period
) {
585 bool start
= (ev
->keyval
== GDK_comma
);
586 bool end
= (ev
->keyval
== GDK_period
);
587 bool shorter
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
);
588 bool fine
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
590 change_note_lengths (fine
, shorter
, 0.0, start
, end
);
594 } else if (ev
->keyval
== GDK_Delete
) {
599 } else if (ev
->keyval
== GDK_Tab
) {
601 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
602 goto_previous_note ();
608 } else if (ev
->keyval
== GDK_Up
) {
610 bool allow_smush
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::TertiaryModifier
);
611 bool fine
= !Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
613 if (Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
)) {
614 change_velocities (true, fine
, allow_smush
);
616 transpose (true, fine
, allow_smush
);
620 } else if (ev
->keyval
== GDK_Down
) {
622 bool allow_smush
= Keyboard::modifier_state_contains (ev
->state
, Keyboard::TertiaryModifier
);
623 bool fine
= !Keyboard::modifier_state_contains (ev
->state
, Keyboard::SecondaryModifier
);
625 if (Keyboard::modifier_state_contains (ev
->state
, Keyboard::PrimaryModifier
)) {
626 change_velocities (false, fine
, allow_smush
);
628 transpose (false, fine
, allow_smush
);
632 } else if (ev
->keyval
== GDK_Left
) {
637 } else if (ev
->keyval
== GDK_Right
) {
642 } else if (ev
->keyval
== GDK_Control_L
) {
651 MidiRegionView::key_release (GdkEventKey
* ev
)
653 if (ev
->keyval
== GDK_Alt_L
|| ev
->keyval
== GDK_Alt_R
) {
661 MidiRegionView::show_list_editor ()
664 _list_editor
= new MidiListEditor (trackview
.session(), midi_region());
666 _list_editor
->present ();
669 /** Add a note to the model, and the view, at a canvas (click) coordinate.
670 * \param x horizontal position in pixels
671 * \param y vertical position in pixels
672 * \param length duration of the note in beats, which will be snapped to the grid
673 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
676 MidiRegionView::create_note_at(double x
, double y
, double length
, bool sh
)
678 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
679 MidiStreamView
* const view
= mtv
->midi_view();
681 double note
= midi_stream_view()->y_to_note(y
);
684 assert(note
<= 127.0);
686 // Start of note in frames relative to region start
687 framepos_t
const start_frames
= snap_frame_to_frame(trackview
.editor().pixel_to_frame(x
));
688 assert(start_frames
>= 0);
691 length
= frames_to_beats(
692 snap_frame_to_frame(start_frames
+ beats_to_frames(length
)) - start_frames
);
694 assert (length
!= 0);
697 length
= frames_to_beats (beats_to_frames (length
) - 1);
700 uint16_t chn_mask
= mtv
->channel_selector().get_selected_channels();
704 /* pick the highest selected channel, unless all channels are selected,
705 which is interpreted to mean channel 1 (zero)
708 for (uint16_t i
= 0; i
< 16; ++i
) {
709 if (chn_mask
& (1<<i
)) {
719 const boost::shared_ptr
<NoteType
> new_note (new NoteType (channel
,
720 frames_to_beats(start_frames
+ _region
->start()), length
,
721 (uint8_t)note
, 0x40));
723 if (_model
->contains (new_note
)) {
727 view
->update_note_range(new_note
->note());
729 MidiModel::DiffCommand
* cmd
= _model
->new_diff_command("add note");
731 _model
->apply_command(*trackview
.session(), cmd
);
733 play_midi_note (new_note
);
737 MidiRegionView::clear_events()
742 for (std::vector
<GhostRegion
*>::iterator g
= ghosts
.begin(); g
!= ghosts
.end(); ++g
) {
743 if ((gr
= dynamic_cast<MidiGhostRegion
*>(*g
)) != 0) {
748 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
753 _pgm_changes
.clear();
755 _optimization_iterator
= _events
.end();
759 MidiRegionView::display_model(boost::shared_ptr
<MidiModel
> model
)
763 content_connection
.disconnect ();
764 _model
->ContentsChanged
.connect (content_connection
, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model
, this), gui_context());
768 if (_enable_display
) {
774 MidiRegionView::start_diff_command(string name
)
776 if (!_diff_command
) {
777 _diff_command
= _model
->new_diff_command(name
);
782 MidiRegionView::diff_add_note(const boost::shared_ptr
<NoteType
> note
, bool selected
, bool show_velocity
)
785 _diff_command
->add(note
);
788 _marked_for_selection
.insert(note
);
791 _marked_for_velocity
.insert(note
);
796 MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent
* ev
)
798 if (_diff_command
&& ev
->note()) {
799 _diff_command
->remove(ev
->note());
804 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent
* ev
,
805 MidiModel::DiffCommand::Property property
,
809 _diff_command
->change (ev
->note(), property
, val
);
814 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent
* ev
,
815 MidiModel::DiffCommand::Property property
,
816 Evoral::MusicalTime val
)
819 _diff_command
->change (ev
->note(), property
, val
);
824 MidiRegionView::apply_diff ()
828 if (!_diff_command
) {
832 if ((add_or_remove
= _diff_command
->adds_or_removes())) {
833 // Mark all selected notes for selection when model reloads
834 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
835 _marked_for_selection
.insert((*i
)->note());
839 _model
->apply_command(*trackview
.session(), _diff_command
);
841 midi_view()->midi_track()->playlist_modified();
844 _marked_for_selection
.clear();
847 _marked_for_velocity
.clear();
851 MidiRegionView::apply_diff_as_subcommand()
855 if (!_diff_command
) {
859 if ((add_or_remove
= _diff_command
->adds_or_removes())) {
860 // Mark all selected notes for selection when model reloads
861 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
862 _marked_for_selection
.insert((*i
)->note());
866 _model
->apply_command_as_subcommand(*trackview
.session(), _diff_command
);
868 midi_view()->midi_track()->playlist_modified();
871 _marked_for_selection
.clear();
873 _marked_for_velocity
.clear();
878 MidiRegionView::abort_command()
880 delete _diff_command
;
886 MidiRegionView::find_canvas_note (boost::shared_ptr
<NoteType
> note
)
888 if (_optimization_iterator
!= _events
.end()) {
889 ++_optimization_iterator
;
892 if (_optimization_iterator
!= _events
.end() && (*_optimization_iterator
)->note() == note
) {
893 return *_optimization_iterator
;
896 for (_optimization_iterator
= _events
.begin(); _optimization_iterator
!= _events
.end(); ++_optimization_iterator
) {
897 if ((*_optimization_iterator
)->note() == note
) {
898 return *_optimization_iterator
;
906 MidiRegionView::get_events (Events
& e
, Evoral::Sequence
<Evoral::MusicalTime
>::NoteOperator op
, uint8_t val
, int chan_mask
)
908 MidiModel::Notes notes
;
909 _model
->get_notes (notes
, op
, val
, chan_mask
);
911 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
912 CanvasNoteEvent
* cne
= find_canvas_note (*n
);
920 MidiRegionView::redisplay_model()
922 // Don't redisplay the model if we're currently recording and displaying that
928 cerr
<< "MidiRegionView::redisplay_model called without a model" << endmsg
;
932 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
936 MidiModel::ReadLock
lock(_model
->read_lock());
938 MidiModel::Notes
& notes (_model
->notes());
939 _optimization_iterator
= _events
.begin();
941 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
943 boost::shared_ptr
<NoteType
> note (*n
);
944 CanvasNoteEvent
* cne
;
947 if (note_in_region_range (note
, visible
)) {
949 if ((cne
= find_canvas_note (note
)) != 0) {
956 if ((cn
= dynamic_cast<CanvasNote
*>(cne
)) != 0) {
958 } else if ((ch
= dynamic_cast<CanvasHit
*>(cne
)) != 0) {
970 add_note (note
, visible
);
975 if ((cne
= find_canvas_note (note
)) != 0) {
983 /* remove note items that are no longer valid */
985 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ) {
986 if (!(*i
)->valid ()) {
988 i
= _events
.erase (i
);
994 _pgm_changes
.clear();
998 display_program_changes();
1000 _marked_for_selection
.clear ();
1001 _marked_for_velocity
.clear ();
1003 /* we may have caused _events to contain things out of order (e.g. if a note
1004 moved earlier or later). we don't generally need them in time order, but
1005 make a note that a sort is required for those cases that require it.
1008 _sort_needed
= true;
1012 MidiRegionView::display_program_changes()
1014 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
1015 uint16_t chn_mask
= mtv
->channel_selector().get_selected_channels();
1017 for (uint8_t i
= 0; i
< 16; ++i
) {
1018 if (chn_mask
& (1<<i
)) {
1019 display_program_changes_on_channel (i
);
1025 MidiRegionView::display_program_changes_on_channel(uint8_t channel
)
1027 boost::shared_ptr
<Evoral::Control
> control
=
1028 _model
->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation
, channel
));
1034 Glib::Mutex::Lock
lock (control
->list()->lock());
1036 for (AutomationList::const_iterator event
= control
->list()->begin();
1037 event
!= control
->list()->end(); ++event
) {
1038 double event_time
= (*event
)->when
;
1039 double program_number
= floor((*event
)->value
+ 0.5);
1041 // Get current value of bank select MSB at time of the program change
1042 Evoral::Parameter
bank_select_msb(MidiCCAutomation
, channel
, MIDI_CTL_MSB_BANK
);
1043 boost::shared_ptr
<Evoral::Control
> msb_control
= _model
->control(bank_select_msb
);
1045 if (msb_control
!= 0) {
1046 msb
= uint8_t(floor(msb_control
->get_double(true, event_time
) + 0.5));
1049 // Get current value of bank select LSB at time of the program change
1050 Evoral::Parameter
bank_select_lsb(MidiCCAutomation
, channel
, MIDI_CTL_LSB_BANK
);
1051 boost::shared_ptr
<Evoral::Control
> lsb_control
= _model
->control(bank_select_lsb
);
1053 if (lsb_control
!= 0) {
1054 lsb
= uint8_t(floor(lsb_control
->get_double(true, event_time
) + 0.5));
1057 MIDI::Name::PatchPrimaryKey
patch_key(msb
, lsb
, program_number
);
1059 boost::shared_ptr
<MIDI::Name::Patch
> patch
=
1060 MIDI::Name::MidiPatchManager::instance().find_patch(
1061 _model_name
, _custom_device_mode
, channel
, patch_key
);
1063 PCEvent
program_change(event_time
, uint8_t(program_number
), channel
);
1066 add_pgm_change(program_change
, patch
->name());
1069 // program_number is zero-based: convert to one-based
1070 snprintf(buf
, 4, "%d", int(program_number
+1));
1071 add_pgm_change(program_change
, buf
);
1077 MidiRegionView::display_sysexes()
1079 for (MidiModel::SysExes::const_iterator i
= _model
->sysexes().begin(); i
!= _model
->sysexes().end(); ++i
) {
1080 Evoral::MusicalTime time
= (*i
)->time();
1085 for (uint32_t b
= 0; b
< (*i
)->size(); ++b
) {
1086 str
<< int((*i
)->buffer()[b
]);
1087 if (b
!= (*i
)->size() -1) {
1091 string text
= str
.str();
1093 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
1095 const double x
= trackview
.editor().frame_to_pixel(beats_to_frames(time
));
1097 double height
= midi_stream_view()->contents_height();
1099 boost::shared_ptr
<CanvasSysEx
> sysex
= boost::shared_ptr
<CanvasSysEx
>(
1100 new CanvasSysEx(*this, *group
, text
, height
, x
, 1.0));
1102 // Show unless program change is beyond the region bounds
1103 if (time
- _region
->start() >= _region
->length() || time
< _region
->start()) {
1109 _sys_exes
.push_back(sysex
);
1114 MidiRegionView::~MidiRegionView ()
1116 in_destructor
= true;
1118 trackview
.editor().hide_verbose_canvas_cursor ();
1120 note_delete_connection
.disconnect ();
1122 delete _list_editor
;
1124 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1126 if (_active_notes
) {
1134 delete _diff_command
;
1135 delete _step_edit_cursor
;
1139 MidiRegionView::region_resized (const PropertyChange
& what_changed
)
1141 RegionView::region_resized(what_changed
);
1143 if (what_changed
.contains (ARDOUR::Properties::position
)) {
1144 set_duration(_region
->length(), 0);
1145 if (_enable_display
) {
1152 MidiRegionView::reset_width_dependent_items (double pixel_width
)
1154 RegionView::reset_width_dependent_items(pixel_width
);
1155 assert(_pixel_width
== pixel_width
);
1157 if (_enable_display
) {
1161 move_step_edit_cursor (_step_edit_cursor_position
);
1162 set_step_edit_cursor_width (_step_edit_cursor_width
);
1166 MidiRegionView::set_height (double height
)
1168 static const double FUDGE
= 2.0;
1169 const double old_height
= _height
;
1170 RegionView::set_height(height
);
1171 _height
= height
- FUDGE
;
1173 apply_note_range(midi_stream_view()->lowest_note(),
1174 midi_stream_view()->highest_note(),
1175 height
!= old_height
+ FUDGE
);
1178 name_pixbuf
->raise_to_top();
1181 for (PgmChanges::iterator x
= _pgm_changes
.begin(); x
!= _pgm_changes
.end(); ++x
) {
1182 (*x
)->set_height (midi_stream_view()->contents_height());
1185 if (_step_edit_cursor
) {
1186 _step_edit_cursor
->property_y2() = midi_stream_view()->contents_height();
1191 /** Apply the current note range from the stream view
1192 * by repositioning/hiding notes as necessary
1195 MidiRegionView::apply_note_range (uint8_t min
, uint8_t max
, bool force
)
1197 if (!_enable_display
) {
1201 if (!force
&& _current_range_min
== min
&& _current_range_max
== max
) {
1205 _current_range_min
= min
;
1206 _current_range_max
= max
;
1208 for (Events::const_iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1209 CanvasNoteEvent
* event
= *i
;
1210 boost::shared_ptr
<NoteType
> note (event
->note());
1212 if (note
->note() < _current_range_min
||
1213 note
->note() > _current_range_max
) {
1219 if (CanvasNote
* cnote
= dynamic_cast<CanvasNote
*>(event
)) {
1221 const double y1
= midi_stream_view()->note_to_y(note
->note());
1222 const double y2
= y1
+ floor(midi_stream_view()->note_height());
1224 cnote
->property_y1() = y1
;
1225 cnote
->property_y2() = y2
;
1227 } else if (CanvasHit
* chit
= dynamic_cast<CanvasHit
*>(event
)) {
1229 const double diamond_size
= update_hit (chit
);
1231 chit
->set_height (diamond_size
);
1237 MidiRegionView::add_ghost (TimeAxisView
& tv
)
1241 double unit_position
= _region
->position () / samples_per_unit
;
1242 MidiTimeAxisView
* mtv
= dynamic_cast<MidiTimeAxisView
*>(&tv
);
1243 MidiGhostRegion
* ghost
;
1245 if (mtv
&& mtv
->midi_view()) {
1246 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1247 to allow having midi notes on top of note lines and waveforms.
1249 ghost
= new MidiGhostRegion (*mtv
->midi_view(), trackview
, unit_position
);
1251 ghost
= new MidiGhostRegion (tv
, trackview
, unit_position
);
1254 ghost
->set_height ();
1255 ghost
->set_duration (_region
->length() / samples_per_unit
);
1256 ghosts
.push_back (ghost
);
1258 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1259 if ((note
= dynamic_cast<CanvasNote
*>(*i
)) != 0) {
1260 ghost
->add_note(note
);
1264 GhostRegion::CatchDeletion
.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost
, this, _1
), gui_context());
1270 /** Begin tracking note state for successive calls to add_event
1273 MidiRegionView::begin_write()
1275 assert(!_active_notes
);
1276 _active_notes
= new CanvasNote
*[128];
1277 for (unsigned i
=0; i
< 128; ++i
) {
1278 _active_notes
[i
] = 0;
1283 /** Destroy note state for add_event
1286 MidiRegionView::end_write()
1288 delete[] _active_notes
;
1290 _marked_for_selection
.clear();
1291 _marked_for_velocity
.clear();
1295 /** Resolve an active MIDI note (while recording).
1298 MidiRegionView::resolve_note(uint8_t note
, double end_time
)
1300 if (midi_view()->note_mode() != Sustained
) {
1304 if (_active_notes
&& _active_notes
[note
]) {
1305 const framepos_t end_time_frames
= beats_to_frames(end_time
) - _region
->start();
1306 _active_notes
[note
]->property_x2() = trackview
.editor().frame_to_pixel(end_time_frames
);
1307 _active_notes
[note
]->property_outline_what() = (guint32
) 0xF; // all edges
1308 _active_notes
[note
] = 0;
1313 /** Extend active notes to rightmost edge of region (if length is changed)
1316 MidiRegionView::extend_active_notes()
1318 if (!_active_notes
) {
1322 for (unsigned i
=0; i
< 128; ++i
) {
1323 if (_active_notes
[i
]) {
1324 _active_notes
[i
]->property_x2() = trackview
.editor().frame_to_pixel(_region
->length());
1331 MidiRegionView::play_midi_note(boost::shared_ptr
<NoteType
> note
)
1333 if (no_sound_notes
|| !trackview
.editor().sound_notes()) {
1337 RouteUI
* route_ui
= dynamic_cast<RouteUI
*> (&trackview
);
1339 if (!route_ui
|| !route_ui
->midi_track()) {
1343 NotePlayer
* np
= new NotePlayer (route_ui
->midi_track());
1349 MidiRegionView::play_midi_chord (vector
<boost::shared_ptr
<NoteType
> > notes
)
1351 if (no_sound_notes
|| !trackview
.editor().sound_notes()) {
1355 RouteUI
* route_ui
= dynamic_cast<RouteUI
*> (&trackview
);
1357 if (!route_ui
|| !route_ui
->midi_track()) {
1361 NotePlayer
* np
= new NotePlayer (route_ui
->midi_track());
1363 for (vector
<boost::shared_ptr
<NoteType
> >::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1372 MidiRegionView::note_in_region_range(const boost::shared_ptr
<NoteType
> note
, bool& visible
) const
1374 const framepos_t note_start_frames
= beats_to_frames(note
->time());
1376 bool outside
= (note_start_frames
- _region
->start() >= _region
->length()) ||
1377 (note_start_frames
< _region
->start());
1379 visible
= (note
->note() >= midi_stream_view()->lowest_note()) &&
1380 (note
->note() <= midi_stream_view()->highest_note());
1386 MidiRegionView::update_note (CanvasNote
* ev
)
1388 boost::shared_ptr
<NoteType
> note
= ev
->note();
1390 const framepos_t note_start_frames
= beats_to_frames(note
->time());
1392 /* trim note display to not overlap the end of its region */
1393 const framepos_t note_end_frames
= min (beats_to_frames (note
->end_time()), _region
->start() + _region
->length());
1395 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1396 const double y1
= midi_stream_view()->note_to_y(note
->note());
1397 const double note_endpixel
= trackview
.editor().frame_to_pixel(note_end_frames
- _region
->start());
1399 ev
->property_x1() = x
;
1400 ev
->property_y1() = y1
;
1401 if (note
->length() > 0) {
1402 ev
->property_x2() = note_endpixel
;
1404 ev
->property_x2() = trackview
.editor().frame_to_pixel(_region
->length());
1406 ev
->property_y2() = y1
+ floor(midi_stream_view()->note_height());
1408 if (note
->length() == 0) {
1409 if (_active_notes
) {
1410 assert(note
->note() < 128);
1411 // If this note is already active there's a stuck note,
1412 // finish the old note rectangle
1413 if (_active_notes
[note
->note()]) {
1414 CanvasNote
* const old_rect
= _active_notes
[note
->note()];
1415 boost::shared_ptr
<NoteType
> old_note
= old_rect
->note();
1416 old_rect
->property_x2() = x
;
1417 old_rect
->property_outline_what() = (guint32
) 0xF;
1419 _active_notes
[note
->note()] = ev
;
1421 /* outline all but right edge */
1422 ev
->property_outline_what() = (guint32
) (0x1 & 0x4 & 0x8);
1424 /* outline all edges */
1425 ev
->property_outline_what() = (guint32
) 0xF;
1430 MidiRegionView::update_hit (CanvasHit
* ev
)
1432 boost::shared_ptr
<NoteType
> note
= ev
->note();
1434 const framepos_t note_start_frames
= beats_to_frames(note
->time());
1435 const double x
= trackview
.editor().frame_to_pixel(note_start_frames
- _region
->start());
1436 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1437 const double y
= midi_stream_view()->note_to_y(note
->note()) + ((diamond_size
-2) / 4.0);
1441 return diamond_size
;
1444 /** Add a MIDI note to the view (with length).
1446 * If in sustained mode, notes with length 0 will be considered active
1447 * notes, and resolve_note should be called when the corresponding note off
1448 * event arrives, to properly display the note.
1451 MidiRegionView::add_note(const boost::shared_ptr
<NoteType
> note
, bool visible
)
1453 CanvasNoteEvent
* event
= 0;
1455 assert(note
->time() >= 0);
1456 assert(midi_view()->note_mode() == Sustained
|| midi_view()->note_mode() == Percussive
);
1458 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
1460 if (midi_view()->note_mode() == Sustained
) {
1462 CanvasNote
* ev_rect
= new CanvasNote(*this, *group
, note
);
1464 update_note (ev_rect
);
1468 MidiGhostRegion
* gr
;
1470 for (std::vector
<GhostRegion
*>::iterator g
= ghosts
.begin(); g
!= ghosts
.end(); ++g
) {
1471 if ((gr
= dynamic_cast<MidiGhostRegion
*>(*g
)) != 0) {
1472 gr
->add_note(ev_rect
);
1476 } else if (midi_view()->note_mode() == Percussive
) {
1478 const double diamond_size
= midi_stream_view()->note_height() / 2.0;
1480 CanvasHit
* ev_diamond
= new CanvasHit(*this, *group
, diamond_size
, note
);
1482 update_hit (ev_diamond
);
1491 if (_marked_for_selection
.find(note
) != _marked_for_selection
.end()) {
1492 note_selected(event
, true);
1495 if (_marked_for_velocity
.find(note
) != _marked_for_velocity
.end()) {
1496 event
->show_velocity();
1498 event
->on_channel_selection_change(_last_channel_selection
);
1499 _events
.push_back(event
);
1510 MidiRegionView::step_add_note (uint8_t channel
, uint8_t number
, uint8_t velocity
,
1511 Evoral::MusicalTime pos
, Evoral::MusicalTime len
)
1513 boost::shared_ptr
<NoteType
> new_note (new NoteType (channel
, pos
, len
, number
, velocity
));
1515 /* potentially extend region to hold new note */
1517 framepos_t end_frame
= _region
->position() + beats_to_frames (new_note
->end_time());
1518 framepos_t region_end
= _region
->position() + _region
->length() - 1;
1520 if (end_frame
> region_end
) {
1521 _region
->set_length (end_frame
- _region
->position(), this);
1524 _marked_for_selection
.clear ();
1527 start_diff_command (_("step add"));
1528 diff_add_note (new_note
, true, false);
1531 // last_step_edit_note = new_note;
1535 MidiRegionView::step_sustain (Evoral::MusicalTime beats
)
1537 change_note_lengths (false, false, beats
, false, true);
1541 MidiRegionView::add_pgm_change(PCEvent
& program
, const string
& displaytext
)
1543 assert(program
.time
>= 0);
1545 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
1546 const double x
= trackview
.editor().frame_to_pixel(beats_to_frames(program
.time
));
1548 double height
= midi_stream_view()->contents_height();
1550 boost::shared_ptr
<CanvasProgramChange
> pgm_change
= boost::shared_ptr
<CanvasProgramChange
>(
1551 new CanvasProgramChange(*this, *group
,
1556 _custom_device_mode
,
1557 program
.time
, program
.channel
, program
.value
));
1559 // Show unless program change is beyond the region bounds
1560 if (program
.time
- _region
->start() >= _region
->length() || program
.time
< _region
->start()) {
1566 _pgm_changes
.push_back(pgm_change
);
1570 MidiRegionView::get_patch_key_at(double time
, uint8_t channel
, MIDI::Name::PatchPrimaryKey
& key
)
1572 Evoral::Parameter
bank_select_msb(MidiCCAutomation
, channel
, MIDI_CTL_MSB_BANK
);
1573 boost::shared_ptr
<Evoral::Control
> msb_control
= _model
->control(bank_select_msb
);
1575 if (msb_control
!= 0) {
1576 msb
= int(msb_control
->get_double(true, time
));
1579 Evoral::Parameter
bank_select_lsb(MidiCCAutomation
, channel
, MIDI_CTL_LSB_BANK
);
1580 boost::shared_ptr
<Evoral::Control
> lsb_control
= _model
->control(bank_select_lsb
);
1582 if (lsb_control
!= 0) {
1583 lsb
= lsb_control
->get_double(true, time
);
1586 Evoral::Parameter
program_change(MidiPgmChangeAutomation
, channel
, 0);
1587 boost::shared_ptr
<Evoral::Control
> program_control
= _model
->control(program_change
);
1588 double program_number
= -1.0;
1589 if (program_control
!= 0) {
1590 program_number
= program_control
->get_double(true, time
);
1593 key
.msb
= (int) floor(msb
+ 0.5);
1594 key
.lsb
= (int) floor(lsb
+ 0.5);
1595 key
.program_number
= (int) floor(program_number
+ 0.5);
1596 assert(key
.is_sane());
1601 MidiRegionView::alter_program_change(PCEvent
& old_program
, const MIDI::Name::PatchPrimaryKey
& new_patch
)
1603 // TODO: Get the real event here and alter them at the original times
1604 Evoral::Parameter
bank_select_msb(MidiCCAutomation
, old_program
.channel
, MIDI_CTL_MSB_BANK
);
1605 boost::shared_ptr
<Evoral::Control
> msb_control
= _model
->control(bank_select_msb
);
1606 if (msb_control
!= 0) {
1607 msb_control
->set_double(double(new_patch
.msb
), true, old_program
.time
);
1610 // TODO: Get the real event here and alter them at the original times
1611 Evoral::Parameter
bank_select_lsb(MidiCCAutomation
, old_program
.channel
, MIDI_CTL_LSB_BANK
);
1612 boost::shared_ptr
<Evoral::Control
> lsb_control
= _model
->control(bank_select_lsb
);
1613 if (lsb_control
!= 0) {
1614 lsb_control
->set_double(double(new_patch
.lsb
), true, old_program
.time
);
1617 Evoral::Parameter
program_change(MidiPgmChangeAutomation
, old_program
.channel
, 0);
1618 boost::shared_ptr
<Evoral::Control
> program_control
= _model
->control(program_change
);
1620 assert(program_control
!= 0);
1621 program_control
->set_double(float(new_patch
.program_number
), true, old_program
.time
);
1623 _pgm_changes
.clear ();
1624 display_program_changes (); // XXX would be nice to limit to just old_program.channel
1628 MidiRegionView::program_selected(CanvasProgramChange
& program
, const MIDI::Name::PatchPrimaryKey
& new_patch
)
1630 PCEvent
program_change_event(program
.event_time(), program
.program(), program
.channel());
1631 alter_program_change(program_change_event
, new_patch
);
1635 MidiRegionView::previous_program(CanvasProgramChange
& program
)
1637 if (program
.program() < 127) {
1638 MIDI::Name::PatchPrimaryKey key
;
1639 get_patch_key_at(program
.event_time(), program
.channel(), key
);
1640 PCEvent
program_change_event(program
.event_time(), program
.program(), program
.channel());
1642 key
.program_number
++;
1643 alter_program_change(program_change_event
, key
);
1648 MidiRegionView::next_program(CanvasProgramChange
& program
)
1650 if (program
.program() > 0) {
1651 MIDI::Name::PatchPrimaryKey key
;
1652 get_patch_key_at(program
.event_time(), program
.channel(), key
);
1653 PCEvent
program_change_event(program
.event_time(), program
.program(), program
.channel());
1655 key
.program_number
--;
1656 alter_program_change(program_change_event
, key
);
1661 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent
* cne
)
1663 if (_selection
.empty()) {
1667 if (_selection
.erase (cne
) > 0) {
1668 cerr
<< "Erased a CNE from selection\n";
1673 MidiRegionView::delete_selection()
1675 if (_selection
.empty()) {
1679 start_diff_command (_("delete selection"));
1681 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1682 if ((*i
)->selected()) {
1683 _diff_command
->remove((*i
)->note());
1693 MidiRegionView::delete_note (boost::shared_ptr
<NoteType
> n
)
1695 start_diff_command (_("delete note"));
1696 _diff_command
->remove (n
);
1699 trackview
.editor().hide_verbose_canvas_cursor ();
1703 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent
* ev
)
1705 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1706 if ((*i
)->selected() && (*i
) != ev
) {
1707 (*i
)->set_selected(false);
1708 (*i
)->hide_velocity();
1716 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent
* ev
)
1718 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
1721 Selection::iterator tmp
= i
;
1724 (*i
)->set_selected (false);
1725 _selection
.erase (i
);
1734 /* don't bother with removing this regionview from the editor selection,
1735 since we're about to add another note, and thus put/keep this
1736 regionview in the editor selection.
1739 if (!ev
->selected()) {
1740 add_to_selection (ev
);
1745 MidiRegionView::select_matching_notes (uint8_t notenum
, uint16_t channel_mask
, bool add
, bool extend
)
1747 uint8_t low_note
= 127;
1748 uint8_t high_note
= 0;
1749 MidiModel::Notes
& notes (_model
->notes());
1750 _optimization_iterator
= _events
.begin();
1756 if (extend
&& _selection
.empty()) {
1762 /* scan existing selection to get note range */
1764 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1765 if ((*i
)->note()->note() < low_note
) {
1766 low_note
= (*i
)->note()->note();
1768 if ((*i
)->note()->note() > high_note
) {
1769 high_note
= (*i
)->note()->note();
1773 low_note
= min (low_note
, notenum
);
1774 high_note
= max (high_note
, notenum
);
1777 no_sound_notes
= true;
1779 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1781 boost::shared_ptr
<NoteType
> note (*n
);
1782 CanvasNoteEvent
* cne
;
1783 bool select
= false;
1785 if (((1 << note
->channel()) & channel_mask
) != 0) {
1787 if ((note
->note() >= low_note
&& note
->note() <= high_note
)) {
1790 } else if (note
->note() == notenum
) {
1796 if ((cne
= find_canvas_note (note
)) != 0) {
1797 // extend is false because we've taken care of it,
1798 // since it extends by time range, not pitch.
1799 note_selected (cne
, add
, false);
1803 add
= true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1807 no_sound_notes
= false;
1811 MidiRegionView::toggle_matching_notes (uint8_t notenum
, uint16_t channel_mask
)
1813 MidiModel::Notes
& notes (_model
->notes());
1814 _optimization_iterator
= _events
.begin();
1816 for (MidiModel::Notes::iterator n
= notes
.begin(); n
!= notes
.end(); ++n
) {
1818 boost::shared_ptr
<NoteType
> note (*n
);
1819 CanvasNoteEvent
* cne
;
1821 if (note
->note() == notenum
&& (((0x0001 << note
->channel()) & channel_mask
) != 0)) {
1822 if ((cne
= find_canvas_note (note
)) != 0) {
1823 if (cne
->selected()) {
1824 note_deselected (cne
);
1826 note_selected (cne
, true, false);
1834 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent
* ev
, bool add
, bool extend
)
1837 clear_selection_except(ev
);
1842 if (!ev
->selected()) {
1843 add_to_selection (ev
);
1847 /* find end of latest note selected, select all between that and the start of "ev" */
1849 Evoral::MusicalTime earliest
= Evoral::MaxMusicalTime
;
1850 Evoral::MusicalTime latest
= 0;
1852 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1853 if ((*i
)->note()->end_time() > latest
) {
1854 latest
= (*i
)->note()->end_time();
1856 if ((*i
)->note()->time() < earliest
) {
1857 earliest
= (*i
)->note()->time();
1861 if (ev
->note()->end_time() > latest
) {
1862 latest
= ev
->note()->end_time();
1865 if (ev
->note()->time() < earliest
) {
1866 earliest
= ev
->note()->time();
1869 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1871 /* find notes entirely within OR spanning the earliest..latest range */
1873 if (((*i
)->note()->time() >= earliest
&& (*i
)->note()->end_time() <= latest
) ||
1874 ((*i
)->note()->time() <= earliest
&& (*i
)->note()->end_time() >= latest
)) {
1875 add_to_selection (*i
);
1879 /* if events were guaranteed to be time sorted, we could do this.
1880 but as of sept 10th 2009, they no longer are.
1883 if ((*i
)->note()->time() > latest
) {
1892 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent
* ev
)
1894 remove_from_selection (ev
);
1898 MidiRegionView::update_drag_selection(double x1
, double x2
, double y1
, double y2
)
1908 // TODO: Make this faster by storing the last updated selection rect, and only
1909 // adjusting things that are in the area that appears/disappeared.
1910 // We probably need a tree to be able to find events in O(log(n)) time.
1912 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
1914 /* check if any corner of the note is inside the rect
1917 1) this is computing "touched by", not "contained by" the rect.
1918 2) this does not require that events be sorted in time.
1921 const double ix1
= (*i
)->x1();
1922 const double ix2
= (*i
)->x2();
1923 const double iy1
= (*i
)->y1();
1924 const double iy2
= (*i
)->y2();
1926 if ((ix1
>= x1
&& ix1
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
1927 (ix1
>= x1
&& ix1
<= x2
&& iy2
>= y1
&& iy2
<= y2
) ||
1928 (ix2
>= x1
&& ix2
<= x2
&& iy1
>= y1
&& iy1
<= y2
) ||
1929 (ix2
>= x1
&& ix2
<= x2
&& iy2
>= y1
&& iy2
<= y2
)) {
1932 if (!(*i
)->selected()) {
1933 add_to_selection (*i
);
1935 } else if ((*i
)->selected()) {
1936 // Not inside rectangle
1937 remove_from_selection (*i
);
1943 MidiRegionView::remove_from_selection (CanvasNoteEvent
* ev
)
1945 Selection::iterator i
= _selection
.find (ev
);
1947 if (i
!= _selection
.end()) {
1948 _selection
.erase (i
);
1951 ev
->set_selected (false);
1952 ev
->hide_velocity ();
1954 if (_selection
.empty()) {
1955 PublicEditor
& editor (trackview
.editor());
1956 editor
.get_selection().remove (this);
1961 MidiRegionView::add_to_selection (CanvasNoteEvent
* ev
)
1963 bool add_mrv_selection
= false;
1965 if (_selection
.empty()) {
1966 add_mrv_selection
= true;
1969 if (_selection
.insert (ev
).second
) {
1970 ev
->set_selected (true);
1971 play_midi_note ((ev
)->note());
1974 if (add_mrv_selection
) {
1975 PublicEditor
& editor (trackview
.editor());
1976 editor
.get_selection().add (this);
1981 MidiRegionView::move_selection(double dx
, double dy
, double cumulative_dy
)
1983 typedef vector
<boost::shared_ptr
<NoteType
> > PossibleChord
;
1984 PossibleChord to_play
;
1985 Evoral::MusicalTime earliest
= Evoral::MaxMusicalTime
;
1987 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1988 if ((*i
)->note()->time() < earliest
) {
1989 earliest
= (*i
)->note()->time();
1993 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
1994 if (Evoral::musical_time_equal ((*i
)->note()->time(), earliest
)) {
1995 to_play
.push_back ((*i
)->note());
1997 (*i
)->move_event(dx
, dy
);
2000 if (dy
&& !_selection
.empty() && !no_sound_notes
&& trackview
.editor().sound_notes()) {
2002 if (to_play
.size() > 1) {
2004 PossibleChord shifted
;
2006 for (PossibleChord::iterator n
= to_play
.begin(); n
!= to_play
.end(); ++n
) {
2007 boost::shared_ptr
<NoteType
> moved_note (new NoteType (**n
));
2008 moved_note
->set_note (moved_note
->note() + cumulative_dy
);
2009 shifted
.push_back (moved_note
);
2012 play_midi_chord (shifted
);
2014 } else if (!to_play
.empty()) {
2016 boost::shared_ptr
<NoteType
> moved_note (new NoteType (*to_play
.front()));
2017 moved_note
->set_note (moved_note
->note() + cumulative_dy
);
2018 play_midi_note (moved_note
);
2024 MidiRegionView::note_dropped(CanvasNoteEvent
*, frameoffset_t dt
, int8_t dnote
)
2026 assert (!_selection
.empty());
2028 uint8_t lowest_note_in_selection
= 127;
2029 uint8_t highest_note_in_selection
= 0;
2030 uint8_t highest_note_difference
= 0;
2032 // find highest and lowest notes first
2034 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2035 uint8_t pitch
= (*i
)->note()->note();
2036 lowest_note_in_selection
= std::min(lowest_note_in_selection
, pitch
);
2037 highest_note_in_selection
= std::max(highest_note_in_selection
, pitch
);
2041 cerr << "dnote: " << (int) dnote << endl;
2042 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2043 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2044 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2045 << int(highest_note_in_selection) << endl;
2046 cerr << "selection size: " << _selection.size() << endl;
2047 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2050 // Make sure the note pitch does not exceed the MIDI standard range
2051 if (highest_note_in_selection
+ dnote
> 127) {
2052 highest_note_difference
= highest_note_in_selection
- 127;
2055 start_diff_command(_("move notes"));
2057 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end() ; ++i
) {
2059 Evoral::MusicalTime new_time
= frames_to_beats (beats_to_frames ((*i
)->note()->time()) + dt
);
2065 diff_add_change (*i
, MidiModel::DiffCommand::StartTime
, new_time
);
2067 uint8_t original_pitch
= (*i
)->note()->note();
2068 uint8_t new_pitch
= original_pitch
+ dnote
- highest_note_difference
;
2070 // keep notes in standard midi range
2071 clamp_to_0_127(new_pitch
);
2073 // keep original pitch if note is dragged outside valid midi range
2074 if ((original_pitch
!= 0 && new_pitch
== 0)
2075 || (original_pitch
!= 127 && new_pitch
== 127)) {
2076 new_pitch
= original_pitch
;
2079 lowest_note_in_selection
= std::min(lowest_note_in_selection
, new_pitch
);
2080 highest_note_in_selection
= std::max(highest_note_in_selection
, new_pitch
);
2082 diff_add_change (*i
, MidiModel::DiffCommand::NoteNumber
, new_pitch
);
2087 // care about notes being moved beyond the upper/lower bounds on the canvas
2088 if (lowest_note_in_selection
< midi_stream_view()->lowest_note() ||
2089 highest_note_in_selection
> midi_stream_view()->highest_note()) {
2090 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange
);
2095 MidiRegionView::snap_pixel_to_frame(double x
)
2097 PublicEditor
& editor
= trackview
.editor();
2098 // x is region relative, convert it to global absolute frames
2099 framepos_t frame
= editor
.pixel_to_frame(x
) + _region
->position();
2100 editor
.snap_to(frame
);
2101 return frame
- _region
->position(); // convert back to region relative
2105 MidiRegionView::snap_frame_to_frame(framepos_t x
)
2107 PublicEditor
& editor
= trackview
.editor();
2108 // x is region relative, convert it to global absolute frames
2109 framepos_t frame
= x
+ _region
->position();
2110 editor
.snap_to(frame
);
2111 return frame
- _region
->position(); // convert back to region relative
2115 MidiRegionView::snap_to_pixel(double x
)
2117 return (double) trackview
.editor().frame_to_pixel(snap_pixel_to_frame(x
));
2121 MidiRegionView::get_position_pixels()
2123 framepos_t region_frame
= get_position();
2124 return trackview
.editor().frame_to_pixel(region_frame
);
2128 MidiRegionView::get_end_position_pixels()
2130 framepos_t frame
= get_position() + get_duration ();
2131 return trackview
.editor().frame_to_pixel(frame
);
2135 MidiRegionView::beats_to_frames(double beats
) const
2137 return _time_converter
.to(beats
);
2141 MidiRegionView::frames_to_beats(framepos_t frames
) const
2143 return _time_converter
.from(frames
);
2147 MidiRegionView::begin_resizing (bool /*at_front*/)
2149 _resize_data
.clear();
2151 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2152 CanvasNote
*note
= dynamic_cast<CanvasNote
*> (*i
);
2154 // only insert CanvasNotes into the map
2156 NoteResizeData
*resize_data
= new NoteResizeData();
2157 resize_data
->canvas_note
= note
;
2159 // create a new SimpleRect from the note which will be the resize preview
2160 SimpleRect
*resize_rect
= new SimpleRect(
2161 *group
, note
->x1(), note
->y1(), note
->x2(), note
->y2());
2163 // calculate the colors: get the color settings
2164 uint32_t fill_color
= UINT_RGBA_CHANGE_A(
2165 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get(),
2168 // make the resize preview notes more transparent and bright
2169 fill_color
= UINT_INTERPOLATE(fill_color
, 0xFFFFFF40, 0.5);
2171 // calculate color based on note velocity
2172 resize_rect
->property_fill_color_rgba() = UINT_INTERPOLATE(
2173 CanvasNoteEvent::meter_style_fill_color(note
->note()->velocity(), note
->selected()),
2177 resize_rect
->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2178 ARDOUR_UI::config()->canvasvar_MidiNoteSelected
.get());
2180 resize_data
->resize_rect
= resize_rect
;
2181 _resize_data
.push_back(resize_data
);
2186 /** Update resizing notes while user drags.
2187 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2188 * @param at_front which end of the note (true == note on, false == note off)
2189 * @param delta_x change in mouse position since the start of the drag
2190 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2191 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2192 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2193 * as the \a primary note.
2196 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote
* primary
, bool at_front
, double delta_x
, bool relative
)
2198 bool cursor_set
= false;
2200 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2201 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2202 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2207 current_x
= canvas_note
->x1() + delta_x
;
2209 current_x
= primary
->x1() + delta_x
;
2213 current_x
= canvas_note
->x2() + delta_x
;
2215 current_x
= primary
->x2() + delta_x
;
2220 resize_rect
->property_x1() = snap_to_pixel(current_x
);
2221 resize_rect
->property_x2() = canvas_note
->x2();
2223 resize_rect
->property_x2() = snap_to_pixel(current_x
);
2224 resize_rect
->property_x1() = canvas_note
->x1();
2230 beats
= snap_pixel_to_frame (current_x
);
2231 beats
= frames_to_beats (beats
);
2236 if (beats
< canvas_note
->note()->end_time()) {
2237 len
= canvas_note
->note()->time() - beats
;
2238 len
+= canvas_note
->note()->length();
2243 if (beats
>= canvas_note
->note()->time()) {
2244 len
= beats
- canvas_note
->note()->time();
2251 snprintf (buf
, sizeof (buf
), "%.3g beats", len
);
2252 trackview
.editor().show_verbose_canvas_cursor_with (buf
);
2261 /** Finish resizing notes when the user releases the mouse button.
2262 * Parameters the same as for \a update_resizing().
2265 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote
* primary
, bool at_front
, double delta_x
, bool relative
)
2267 start_diff_command(_("resize notes"));
2269 for (std::vector
<NoteResizeData
*>::iterator i
= _resize_data
.begin(); i
!= _resize_data
.end(); ++i
) {
2270 CanvasNote
* canvas_note
= (*i
)->canvas_note
;
2271 SimpleRect
* resize_rect
= (*i
)->resize_rect
;
2276 current_x
= canvas_note
->x1() + delta_x
;
2278 current_x
= primary
->x1() + delta_x
;
2282 current_x
= canvas_note
->x2() + delta_x
;
2284 current_x
= primary
->x2() + delta_x
;
2288 current_x
= snap_pixel_to_frame (current_x
);
2289 current_x
= frames_to_beats (current_x
);
2291 if (at_front
&& current_x
< canvas_note
->note()->end_time()) {
2292 diff_add_change (canvas_note
, MidiModel::DiffCommand::StartTime
, current_x
);
2294 double len
= canvas_note
->note()->time() - current_x
;
2295 len
+= canvas_note
->note()->length();
2298 /* XXX convert to beats */
2299 diff_add_change (canvas_note
, MidiModel::DiffCommand::Length
, len
);
2304 double len
= current_x
- canvas_note
->note()->time();
2307 /* XXX convert to beats */
2308 diff_add_change (canvas_note
, MidiModel::DiffCommand::Length
, len
);
2316 _resize_data
.clear();
2321 MidiRegionView::change_note_channel (CanvasNoteEvent
* event
, int8_t channel
)
2323 diff_add_change (event
, MidiModel::DiffCommand::Channel
, (uint8_t) channel
);
2327 MidiRegionView::change_note_velocity(CanvasNoteEvent
* event
, int8_t velocity
, bool relative
)
2329 uint8_t new_velocity
;
2332 new_velocity
= event
->note()->velocity() + velocity
;
2333 clamp_to_0_127(new_velocity
);
2335 new_velocity
= velocity
;
2338 event
->set_selected (event
->selected()); // change color
2340 diff_add_change (event
, MidiModel::DiffCommand::Velocity
, new_velocity
);
2344 MidiRegionView::change_note_note (CanvasNoteEvent
* event
, int8_t note
, bool relative
)
2349 new_note
= event
->note()->note() + note
;
2354 clamp_to_0_127 (new_note
);
2355 diff_add_change (event
, MidiModel::DiffCommand::NoteNumber
, new_note
);
2359 MidiRegionView::trim_note (CanvasNoteEvent
* event
, Evoral::MusicalTime front_delta
, Evoral::MusicalTime end_delta
)
2361 bool change_start
= false;
2362 bool change_length
= false;
2363 Evoral::MusicalTime new_start
= 0;
2364 Evoral::MusicalTime new_length
= 0;
2366 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2368 front_delta: if positive - move the start of the note later in time (shortening it)
2369 if negative - move the start of the note earlier in time (lengthening it)
2371 end_delta: if positive - move the end of the note later in time (lengthening it)
2372 if negative - move the end of the note earlier in time (shortening it)
2376 if (front_delta
< 0) {
2378 if (event
->note()->time() < -front_delta
) {
2381 new_start
= event
->note()->time() + front_delta
; // moves earlier
2384 /* start moved toward zero, so move the end point out to where it used to be.
2385 Note that front_delta is negative, so this increases the length.
2388 new_length
= event
->note()->length() - front_delta
;
2389 change_start
= true;
2390 change_length
= true;
2394 Evoral::MusicalTime new_pos
= event
->note()->time() + front_delta
;
2396 if (new_pos
< event
->note()->end_time()) {
2397 new_start
= event
->note()->time() + front_delta
;
2398 /* start moved toward the end, so move the end point back to where it used to be */
2399 new_length
= event
->note()->length() - front_delta
;
2400 change_start
= true;
2401 change_length
= true;
2408 bool can_change
= true;
2409 if (end_delta
< 0) {
2410 if (event
->note()->length() < -end_delta
) {
2416 new_length
= event
->note()->length() + end_delta
;
2417 change_length
= true;
2422 diff_add_change (event
, MidiModel::DiffCommand::StartTime
, new_start
);
2425 if (change_length
) {
2426 diff_add_change (event
, MidiModel::DiffCommand::Length
, new_length
);
2431 MidiRegionView::change_note_time (CanvasNoteEvent
* event
, Evoral::MusicalTime delta
, bool relative
)
2433 Evoral::MusicalTime new_time
;
2437 if (event
->note()->time() < -delta
) {
2440 new_time
= event
->note()->time() + delta
;
2443 new_time
= event
->note()->time() + delta
;
2449 diff_add_change (event
, MidiModel::DiffCommand::StartTime
, new_time
);
2453 MidiRegionView::change_note_length (CanvasNoteEvent
* event
, Evoral::MusicalTime t
)
2455 diff_add_change (event
, MidiModel::DiffCommand::Length
, t
);
2459 MidiRegionView::change_velocities (bool up
, bool fine
, bool allow_smush
)
2463 if (_selection
.empty()) {
2478 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2479 if ((*i
)->note()->velocity() + delta
== 0 || (*i
)->note()->velocity() + delta
== 127) {
2485 start_diff_command(_("change velocities"));
2487 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end();) {
2488 Selection::iterator next
= i
;
2490 change_note_velocity (*i
, delta
, true);
2496 if (!_selection
.empty()) {
2498 snprintf (buf
, sizeof (buf
), "Vel %d",
2499 (int) (*_selection
.begin())->note()->velocity());
2500 trackview
.editor().show_verbose_canvas_cursor_with (buf
, 10, 10);
2506 MidiRegionView::transpose (bool up
, bool fine
, bool allow_smush
)
2508 if (_selection
.empty()) {
2525 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2527 if ((int8_t) (*i
)->note()->note() + delta
<= 0) {
2531 if ((int8_t) (*i
)->note()->note() + delta
> 127) {
2538 start_diff_command (_("transpose"));
2540 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2541 Selection::iterator next
= i
;
2543 change_note_note (*i
, delta
, true);
2551 MidiRegionView::change_note_lengths (bool fine
, bool shorter
, Evoral::MusicalTime delta
, bool start
, bool end
)
2557 /* grab the current grid distance */
2559 delta
= trackview
.editor().get_grid_type_as_beats (success
, _region
->position());
2561 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2562 cerr
<< "Grid type not available as beats - TO BE FIXED\n";
2572 start_diff_command (_("change note lengths"));
2574 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2575 Selection::iterator next
= i
;
2578 /* note the negation of the delta for start */
2580 trim_note (*i
, (start
? -delta
: 0), (end
? delta
: 0));
2589 MidiRegionView::nudge_notes (bool forward
)
2591 if (_selection
.empty()) {
2595 /* pick a note as the point along the timeline to get the nudge distance.
2596 its not necessarily the earliest note, so we may want to pull the notes out
2597 into a vector and sort before using the first one.
2600 framepos_t ref_point
= _region
->position() + beats_to_frames ((*(_selection
.begin()))->note()->time());
2602 framepos_t distance
;
2604 if (trackview
.editor().snap_mode() == Editing::SnapOff
) {
2606 /* grid is off - use nudge distance */
2608 distance
= trackview
.editor().get_nudge_distance (ref_point
, unused
);
2614 framepos_t next_pos
= ref_point
;
2617 if (max_framepos
- 1 < next_pos
) {
2621 if (next_pos
== 0) {
2627 trackview
.editor().snap_to (next_pos
, (forward
? 1 : -1), false);
2628 distance
= ref_point
- next_pos
;
2631 if (distance
== 0) {
2635 Evoral::MusicalTime delta
= frames_to_beats (fabs (distance
));
2641 start_diff_command (_("nudge"));
2643 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ) {
2644 Selection::iterator next
= i
;
2646 change_note_time (*i
, delta
, true);
2654 MidiRegionView::change_channel(uint8_t channel
)
2656 start_diff_command(_("change channel"));
2657 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2658 diff_add_change (*i
, MidiModel::DiffCommand::Channel
, channel
);
2666 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent
* ev
)
2668 Editor
* editor
= dynamic_cast<Editor
*>(&trackview
.editor());
2670 pre_enter_cursor
= editor
->get_canvas_cursor ();
2672 if (_mouse_state
== SelectTouchDragging
) {
2673 note_selected (ev
, true);
2676 show_verbose_canvas_cursor (ev
->note ());
2680 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent
* note
)
2682 Editor
* editor
= dynamic_cast<Editor
*>(&trackview
.editor());
2684 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2685 (*i
)->hide_velocity ();
2688 editor
->hide_verbose_canvas_cursor ();
2690 if (pre_enter_cursor
) {
2691 editor
->set_canvas_cursor (pre_enter_cursor
);
2692 pre_enter_cursor
= 0;
2697 MidiRegionView::note_mouse_position (float x_fraction
, float y_fraction
, bool can_set_cursor
)
2699 Editor
* editor
= dynamic_cast<Editor
*>(&trackview
.editor());
2701 if (x_fraction
> 0.0 && x_fraction
< 0.25) {
2702 editor
->set_canvas_cursor (editor
->left_side_trim_cursor
);
2703 } else if (x_fraction
>= 0.75 && x_fraction
< 1.0) {
2704 editor
->set_canvas_cursor (editor
->right_side_trim_cursor
);
2706 if (pre_enter_cursor
&& can_set_cursor
) {
2707 editor
->set_canvas_cursor (pre_enter_cursor
);
2713 MidiRegionView::set_frame_color()
2716 if (_selected
&& should_show_selection
) {
2717 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase
.get();
2719 frame
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase
.get();
2725 MidiRegionView::midi_channel_mode_changed(ChannelMode mode
, uint16_t mask
)
2729 case FilterChannels
:
2730 _force_channel
= -1;
2733 _force_channel
= mask
;
2734 mask
= 0xFFFF; // Show all notes as active (below)
2737 // Update notes for selection
2738 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2739 (*i
)->on_channel_selection_change(mask
);
2742 _last_channel_selection
= mask
;
2746 MidiRegionView::midi_patch_settings_changed(std::string model
, std::string custom_device_mode
)
2748 _model_name
= model
;
2749 _custom_device_mode
= custom_device_mode
;
2754 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op
)
2756 if (_selection
.empty()) {
2760 PublicEditor
& editor (trackview
.editor());
2765 editor
.get_cut_buffer().add (selection_as_cut_buffer());
2773 start_diff_command();
2775 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2781 diff_remove_note (*i
);
2791 MidiRegionView::selection_as_cut_buffer () const
2795 for (Selection::iterator i
= _selection
.begin(); i
!= _selection
.end(); ++i
) {
2796 NoteType
* n
= (*i
)->note().get();
2797 notes
.insert (boost::shared_ptr
<NoteType
> (new NoteType (*n
)));
2800 MidiCutBuffer
* cb
= new MidiCutBuffer (trackview
.session());
2807 MidiRegionView::paste (framepos_t pos
, float times
, const MidiCutBuffer
& mcb
)
2813 start_diff_command (_("paste"));
2815 Evoral::MusicalTime beat_delta
;
2816 Evoral::MusicalTime paste_pos_beats
;
2817 Evoral::MusicalTime duration
;
2818 Evoral::MusicalTime end_point
= 0;
2820 duration
= (*mcb
.notes().rbegin())->end_time() - (*mcb
.notes().begin())->time();
2821 paste_pos_beats
= frames_to_beats (pos
- _region
->position());
2822 beat_delta
= (*mcb
.notes().begin())->time() - paste_pos_beats
;
2823 paste_pos_beats
= 0;
2827 for (int n
= 0; n
< (int) times
; ++n
) {
2829 for (Notes::const_iterator i
= mcb
.notes().begin(); i
!= mcb
.notes().end(); ++i
) {
2831 boost::shared_ptr
<NoteType
> copied_note (new NoteType (*((*i
).get())));
2832 copied_note
->set_time (paste_pos_beats
+ copied_note
->time() - beat_delta
);
2834 /* make all newly added notes selected */
2836 diff_add_note (copied_note
, true);
2837 end_point
= copied_note
->end_time();
2840 paste_pos_beats
+= duration
;
2843 /* if we pasted past the current end of the region, extend the region */
2845 framepos_t end_frame
= _region
->position() + beats_to_frames (end_point
);
2846 framepos_t region_end
= _region
->position() + _region
->length() - 1;
2848 if (end_frame
> region_end
) {
2850 trackview
.session()->begin_reversible_command (_("paste"));
2852 _region
->clear_changes ();
2853 _region
->set_length (end_frame
, this);
2854 trackview
.session()->add_command (new StatefulDiffCommand (_region
));
2860 struct EventNoteTimeEarlyFirstComparator
{
2861 bool operator() (CanvasNoteEvent
* a
, CanvasNoteEvent
* b
) {
2862 return a
->note()->time() < b
->note()->time();
2867 MidiRegionView::time_sort_events ()
2869 if (!_sort_needed
) {
2873 EventNoteTimeEarlyFirstComparator cmp
;
2876 _sort_needed
= false;
2880 MidiRegionView::goto_next_note ()
2882 // framepos_t pos = -1;
2883 bool use_next
= false;
2885 if (_events
.back()->selected()) {
2889 time_sort_events ();
2891 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2892 if ((*i
)->selected()) {
2895 } else if (use_next
) {
2897 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2902 /* use the first one */
2904 unique_select (_events
.front());
2909 MidiRegionView::goto_previous_note ()
2911 // framepos_t pos = -1;
2912 bool use_next
= false;
2914 if (_events
.front()->selected()) {
2918 time_sort_events ();
2920 for (Events::reverse_iterator i
= _events
.rbegin(); i
!= _events
.rend(); ++i
) {
2921 if ((*i
)->selected()) {
2924 } else if (use_next
) {
2926 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2931 /* use the last one */
2933 unique_select (*(_events
.rbegin()));
2937 MidiRegionView::selection_as_notelist (Notes
& selected
, bool allow_all_if_none_selected
)
2939 bool had_selected
= false;
2941 time_sort_events ();
2943 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2944 if ((*i
)->selected()) {
2945 selected
.insert ((*i
)->note());
2946 had_selected
= true;
2950 if (allow_all_if_none_selected
&& !had_selected
) {
2951 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
2952 selected
.insert ((*i
)->note());
2958 MidiRegionView::update_ghost_note (double x
, double y
)
2964 framepos_t f
= trackview
.editor().pixel_to_frame (x
) + _region
->position ();
2965 trackview
.editor().snap_to (f
);
2966 f
-= _region
->position ();
2969 Evoral::MusicalTime beats
= trackview
.editor().get_grid_type_as_beats (success
, f
);
2974 double length
= frames_to_beats (snap_frame_to_frame (f
+ beats_to_frames (beats
)) - f
);
2976 _ghost_note
->note()->set_time (frames_to_beats (f
+ _region
->start()));
2977 _ghost_note
->note()->set_length (length
);
2978 _ghost_note
->note()->set_note (midi_stream_view()->y_to_note (y
));
2980 update_note (_ghost_note
);
2982 show_verbose_canvas_cursor (_ghost_note
->note ());
2986 MidiRegionView::create_ghost_note (double x
, double y
)
2991 boost::shared_ptr
<NoteType
> g (new NoteType
);
2992 _ghost_note
= new NoEventCanvasNote (*this, *group
, g
);
2993 update_ghost_note (x
, y
);
2994 _ghost_note
->show ();
2999 show_verbose_canvas_cursor (_ghost_note
->note ());
3003 MidiRegionView::snap_changed ()
3009 create_ghost_note (_last_ghost_x
, _last_ghost_y
);
3013 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr
<NoteType
> n
) const
3016 snprintf (buf
, sizeof (buf
), "%s (%d)\nVel %d",
3017 Evoral::midi_note_name (n
->note()).c_str(),
3019 (int) n
->velocity());
3020 trackview
.editor().show_verbose_canvas_cursor_with (buf
, 10, 20);
3024 MidiRegionView::drop_down_keys ()
3026 _mouse_state
= None
;
3030 MidiRegionView::maybe_select_by_position (GdkEventButton
* ev
, double x
, double y
)
3032 double note
= midi_stream_view()->y_to_note(y
);
3034 MidiTimeAxisView
* const mtv
= dynamic_cast<MidiTimeAxisView
*>(&trackview
);
3036 cerr
<< "Selecting by position\n";
3038 uint16_t chn_mask
= mtv
->channel_selector().get_selected_channels();
3040 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::TertiaryModifier
)) {
3041 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchGreaterThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
3042 } else if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
3043 get_events (e
, Evoral::Sequence
<Evoral::MusicalTime
>::PitchLessThanOrEqual
, (uint8_t) floor (note
), chn_mask
);
3048 bool add_mrv_selection
= false;
3050 if (_selection
.empty()) {
3051 add_mrv_selection
= true;
3054 for (Events::iterator i
= e
.begin(); i
!= e
.end(); ++i
) {
3055 if (_selection
.insert (*i
).second
) {
3056 (*i
)->set_selected (true);
3060 if (add_mrv_selection
) {
3061 PublicEditor
& editor (trackview
.editor());
3062 editor
.get_selection().add (this);
3067 MidiRegionView::color_handler ()
3069 RegionView::color_handler ();
3071 for (Events::iterator i
= _events
.begin(); i
!= _events
.end(); ++i
) {
3072 (*i
)->set_selected ((*i
)->selected()); // will change color
3075 /* XXX probably more to do here */
3079 MidiRegionView::enable_display (bool yn
)
3081 RegionView::enable_display (yn
);
3088 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos
)
3090 if (_step_edit_cursor
== 0) {
3091 ArdourCanvas::Group
* const group
= (ArdourCanvas::Group
*)get_canvas_group();
3093 _step_edit_cursor
= new ArdourCanvas::SimpleRect (*group
);
3094 _step_edit_cursor
->property_y1() = 0;
3095 _step_edit_cursor
->property_y2() = midi_stream_view()->contents_height();
3096 _step_edit_cursor
->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3097 _step_edit_cursor
->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3100 move_step_edit_cursor (pos
);
3101 _step_edit_cursor
->show ();
3105 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos
)
3107 _step_edit_cursor_position
= pos
;
3109 if (_step_edit_cursor
) {
3110 double pixel
= trackview
.editor().frame_to_pixel (beats_to_frames (pos
));
3111 _step_edit_cursor
->property_x1() = pixel
;
3112 set_step_edit_cursor_width (_step_edit_cursor_width
);
3117 MidiRegionView::hide_step_edit_cursor ()
3119 if (_step_edit_cursor
) {
3120 _step_edit_cursor
->hide ();
3125 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats
)
3127 _step_edit_cursor_width
= beats
;
3129 if (_step_edit_cursor
) {
3130 _step_edit_cursor
->property_x2() = _step_edit_cursor
->property_x1() + trackview
.editor().frame_to_pixel (beats_to_frames (beats
));
3134 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3135 * @param buf Data that has been recorded.
3136 * @param w Source that this data will end up in.
3139 MidiRegionView::data_recorded (boost::shared_ptr
<MidiBuffer
> buf
, boost::weak_ptr
<MidiSource
> w
)
3141 if (!_active_notes
) {
3142 /* we aren't actively being recorded to */
3146 boost::shared_ptr
<MidiSource
> src
= w
.lock ();
3147 if (!src
|| src
!= midi_region()->midi_source()) {
3148 /* recorded data was not destined for our source */
3152 MidiTimeAxisView
* mtv
= dynamic_cast<MidiTimeAxisView
*> (&trackview
);
3153 BeatsFramesConverter
converter (trackview
.session()->tempo_map(), mtv
->midi_track()->get_capture_start_frame (0));
3155 for (MidiBuffer::iterator i
= buf
->begin(); i
!= buf
->end(); ++i
) {
3156 Evoral::MIDIEvent
<MidiBuffer::TimeType
> const ev (*i
, false);
3157 assert (ev
.buffer ());
3159 Evoral::MusicalTime
const time_beats
= converter
.from (ev
.time () - converter
.origin_b ());
3161 if (ev
.type() == MIDI_CMD_NOTE_ON
) {
3163 boost::shared_ptr
<Evoral::Note
<Evoral::MusicalTime
> > note (
3164 new Evoral::Note
<Evoral::MusicalTime
> (ev
.channel(), time_beats
, 0, ev
.note(), ev
.velocity())
3167 add_note (note
, true);
3169 /* fix up our note range */
3170 if (ev
.note() < _current_range_min
) {
3171 midi_stream_view()->apply_note_range (ev
.note(), _current_range_max
, true);
3172 } else if (ev
.note() > _current_range_max
) {
3173 midi_stream_view()->apply_note_range (_current_range_min
, ev
.note(), true);
3176 } else if (ev
.type() == MIDI_CMD_NOTE_OFF
) {
3177 resolve_note (ev
.note (), time_beats
);