2 Copyright (C) 2000 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <sigc++/bind.h>
28 #include "pbd/error.h"
29 #include "pbd/stl_delete.h"
30 #include "pbd/whitespace.h"
31 #include "pbd/basename.h"
32 #include "pbd/enumwriter.h"
33 #include "pbd/memento_command.h"
34 #include "pbd/stateful_diff_command.h"
36 #include "gtkmm2ext/gtk_ui.h"
37 #include "gtkmm2ext/selector.h"
38 #include "gtkmm2ext/bindable_button.h"
39 #include "gtkmm2ext/utils.h"
41 #include "ardour/file_source.h"
42 #include "ardour/midi_playlist.h"
43 #include "ardour/midi_diskstream.h"
44 #include "ardour/midi_patch_manager.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/processor.h"
47 #include "ardour/ladspa_plugin.h"
48 #include "ardour/location.h"
49 #include "ardour/playlist.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlist.h"
53 #include "ardour/tempo.h"
54 #include "ardour/utils.h"
55 #include "ardour/operations.h"
57 #include "midi++/names.h"
59 #include "add_midi_cc_track_dialog.h"
60 #include "ardour_ui.h"
61 #include "automation_line.h"
62 #include "automation_time_axis.h"
63 #include "canvas-note-event.h"
64 #include "canvas_impl.h"
65 #include "crossfade_view.h"
68 #include "ghostregion.h"
69 #include "gui_thread.h"
71 #include "midi_scroomer.h"
72 #include "midi_streamview.h"
73 #include "midi_region_view.h"
74 #include "midi_time_axis.h"
75 #include "piano_roll_header.h"
76 #include "playlist_selector.h"
77 #include "plugin_selector.h"
78 #include "plugin_ui.h"
79 #include "point_selection.h"
81 #include "region_view.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "step_editor.h"
85 #include "simplerect.h"
88 #include "ardour/midi_track.h"
92 using namespace ARDOUR
;
95 using namespace Gtkmm2ext
;
96 using namespace Editing
;
98 // Minimum height at which a control is displayed
99 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT
= 162;
100 static const uint32_t KEYBOARD_MIN_HEIGHT
= 140;
102 MidiTimeAxisView::MidiTimeAxisView (PublicEditor
& ed
, Session
* sess
, Canvas
& canvas
)
103 : AxisView(sess
) // virtually inherited
104 , RouteTimeAxisView(ed
, sess
, canvas
)
105 , _ignore_signals(false)
107 , _piano_roll_header(0)
108 , _note_mode(Sustained
)
110 , _percussion_mode_item(0)
111 , _color_mode(MeterColors
)
112 , _meter_color_mode_item(0)
113 , _channel_color_mode_item(0)
114 , _track_color_mode_item(0)
115 , _step_edit_item (0)
116 , _midi_thru_item (0)
117 , controller_menu (0)
123 MidiTimeAxisView::set_route (boost::shared_ptr
<Route
> rt
)
125 RouteTimeAxisView::set_route (rt
);
127 subplugin_menu
.set_name ("ArdourContextMenu");
129 _view
= new MidiStreamView (*this);
130 if (!gui_property ("note-range-min").empty ()) {
131 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
133 midi_view()->NoteRangeChanged
.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed
));
135 ignore_toggle
= false;
137 mute_button
->set_active (false);
138 solo_button
->set_active (false);
140 if (is_midi_track()) {
141 controls_ebox
.set_name ("MidiTimeAxisViewControlsBaseUnselected");
142 _note_mode
= midi_track()->note_mode();
143 } else { // MIDI bus (which doesn't exist yet..)
144 controls_ebox
.set_name ("MidiBusControlsBaseUnselected");
147 /* map current state of the route */
149 processors_changed (RouteProcessorChange ());
151 _route
->processors_changed
.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed
, this, _1
), gui_context());
154 _piano_roll_header
= new PianoRollHeader(*midi_view());
156 _piano_roll_header
->AddNoteSelection
.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection
));
157 _piano_roll_header
->ExtendNoteSelection
.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection
));
158 _piano_roll_header
->ToggleNoteSelection
.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection
));
160 _range_scroomer
= new MidiScroomer(midi_view()->note_range_adjustment
);
162 /* Suspend updates of the StreamView during scroomer drags to speed things up */
163 _range_scroomer
->DragStarting
.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates
));
164 _range_scroomer
->DragFinishing
.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates
));
166 controls_hbox
.pack_start(*_range_scroomer
);
167 controls_hbox
.pack_start(*_piano_roll_header
);
169 controls_ebox
.set_name ("MidiTrackControlsBaseUnselected");
170 controls_base_selected_name
= "MidiTrackControlsBaseSelected";
171 controls_base_unselected_name
= "MidiTrackControlsBaseUnselected";
173 midi_view()->NoteRangeChanged
.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range
));
175 /* ask for notifications of any new RegionViews */
176 _view
->RegionViewAdded
.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added
));
178 if (!_editor
.have_idled()) {
179 /* first idle will do what we need */
185 HBox
* midi_controls_hbox
= manage(new HBox());
187 MIDI::Name::MidiPatchManager
& patch_manager
= MIDI::Name::MidiPatchManager::instance();
189 MIDI::Name::MasterDeviceNames::Models::const_iterator m
= patch_manager
.all_models().begin();
190 for (; m
!= patch_manager
.all_models().end(); ++m
) {
191 _model_selector
.append_text(m
->c_str());
194 _model_selector
.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed
));
196 _custom_device_mode_selector
.signal_changed().connect(
197 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed
));
199 // TODO: persist the choice
200 // this initializes the comboboxes and sends out the signal
201 _model_selector
.set_active(0);
203 midi_controls_hbox
->pack_start(_channel_selector
, true, false);
204 if (!patch_manager
.all_models().empty()) {
205 _midi_controls_box
.pack_start(_model_selector
, true, false);
206 _midi_controls_box
.pack_start(_custom_device_mode_selector
, true, false);
209 _midi_controls_box
.pack_start(*midi_controls_hbox
, true, true);
211 controls_vbox
.pack_start(_midi_controls_box
, false, false);
213 // restore channel selector settings
214 _channel_selector
.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
215 _channel_selector
.mode_changed
.connect(
216 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode
));
217 _channel_selector
.mode_changed
.connect(
218 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode
));
220 string prop
= gui_property ("color-mode");
222 _color_mode
= ColorMode (string_2_enum(prop
, _color_mode
));
223 if (_color_mode
== ChannelColors
) {
224 _channel_selector
.set_channel_colors(CanvasNoteEvent::midi_channel_colors
);
228 set_color_mode (_color_mode
, true, false);
230 prop
= gui_property ("note-mode");
232 _note_mode
= NoteMode (string_2_enum (prop
, _note_mode
));
233 if (_percussion_mode_item
) {
234 _percussion_mode_item
->set_active (_note_mode
== Percussive
);
238 /* Look for any GUI object state nodes that represent automation children that should exist, and create
242 GUIObjectState
& gui_state
= gui_object_state ();
243 for (GUIObjectState::StringPropertyMap::const_iterator i
= gui_state
.begin(); i
!= gui_state
.end(); ++i
) {
246 Evoral::Parameter
parameter (0, 0, 0);
248 bool const p
= AutomationTimeAxisView::parse_state_id (i
->first
, route_id
, has_parameter
, parameter
);
249 if (p
&& route_id
== _route
->id () && has_parameter
) {
250 create_automation_child (parameter
, string_is_affirmative (gui_object_state().get_string (i
->first
, X_("visible"))));
256 MidiTimeAxisView::first_idle ()
263 MidiTimeAxisView::~MidiTimeAxisView ()
265 delete _piano_roll_header
;
266 _piano_roll_header
= 0;
268 delete _range_scroomer
;
271 delete controller_menu
;
276 MidiTimeAxisView::enter_internal_edit_mode ()
279 midi_view()->enter_internal_edit_mode ();
284 MidiTimeAxisView::leave_internal_edit_mode ()
287 midi_view()->leave_internal_edit_mode ();
292 MidiTimeAxisView::check_step_edit ()
294 ensure_step_editor ();
295 _step_editor
->check_step_edit ();
299 MidiTimeAxisView::model_changed()
301 std::list
<std::string
> device_modes
= MIDI::Name::MidiPatchManager::instance()
302 .custom_device_mode_names_by_model(_model_selector
.get_active_text());
304 _custom_device_mode_selector
.clear_items();
306 for (std::list
<std::string
>::const_iterator i
= device_modes
.begin();
307 i
!= device_modes
.end(); ++i
) {
308 cerr
<< "found custom device mode " << *i
<< " thread_id: " << pthread_self() << endl
;
309 _custom_device_mode_selector
.append_text(*i
);
312 _custom_device_mode_selector
.set_active(0);
315 void MidiTimeAxisView::custom_device_mode_changed()
317 _midi_patch_settings_changed
.emit(_model_selector
.get_active_text(),
318 _custom_device_mode_selector
.get_active_text());
322 MidiTimeAxisView::midi_view()
324 return dynamic_cast<MidiStreamView
*>(_view
);
328 MidiTimeAxisView::set_height (uint32_t h
)
330 RouteTimeAxisView::set_height (h
);
332 if (height
>= MIDI_CONTROLS_BOX_MIN_HEIGHT
) {
333 _midi_controls_box
.show_all ();
335 _midi_controls_box
.hide();
338 if (height
>= KEYBOARD_MIN_HEIGHT
) {
339 if (is_track() && _range_scroomer
) {
340 _range_scroomer
->show();
342 if (is_track() && _piano_roll_header
) {
343 _piano_roll_header
->show();
346 if (is_track() && _range_scroomer
) {
347 _range_scroomer
->hide();
349 if (is_track() && _piano_roll_header
) {
350 _piano_roll_header
->hide();
356 MidiTimeAxisView::append_extra_display_menu_items ()
358 using namespace Menu_Helpers
;
360 MenuList
& items
= display_menu
->items();
363 Menu
*range_menu
= manage(new Menu
);
364 MenuList
& range_items
= range_menu
->items();
365 range_menu
->set_name ("ArdourContextMenu");
367 range_items
.push_back (MenuElem (_("Show Full Range"), sigc::bind (
368 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range
),
369 MidiStreamView::FullRange
)));
371 range_items
.push_back (MenuElem (_("Fit Contents"), sigc::bind (
372 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range
),
373 MidiStreamView::ContentsRange
)));
375 items
.push_back (MenuElem (_("Note Range"), *range_menu
));
376 items
.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
378 items
.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru
)));
379 _midi_thru_item
= dynamic_cast<CheckMenuItem
*>(&items
.back());
381 items
.push_back (SeparatorElem ());
385 MidiTimeAxisView::toggle_midi_thru ()
387 if (!_midi_thru_item
) {
391 bool view_yn
= _midi_thru_item
->get_active();
392 if (view_yn
!= midi_track()->midi_thru()) {
393 midi_track()->set_midi_thru (view_yn
);
398 MidiTimeAxisView::build_automation_action_menu (bool for_selection
)
400 using namespace Menu_Helpers
;
402 /* If we have a controller menu, we need to detach it before
403 RouteTimeAxis::build_automation_action_menu destroys the
404 menu it is attached to. Otherwise GTK destroys
405 controller_menu's gobj, meaning that it can't be reattached
406 below. See bug #3134.
409 if (controller_menu
) {
410 detach_menu (*controller_menu
);
413 _channel_command_menu_map
.clear ();
414 RouteTimeAxisView::build_automation_action_menu (for_selection
);
416 MenuList
& automation_items
= automation_action_menu
->items();
418 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
420 if (selected_channels
!= 0) {
422 automation_items
.push_back (SeparatorElem());
424 /* these 2 MIDI "command" types are semantically more like automation than note data,
425 but they are not MIDI controllers. We give them special status in this menu, since
426 they will not show up in the controller list and anyone who actually knows
427 something about MIDI (!) would not expect to find them there.
430 add_channel_command_menu_item (automation_items
, _("Bender"), MidiPitchBenderAutomation
, 0);
431 automation_items
.back().set_sensitive (!for_selection
|| _editor
.get_selection().tracks
.size() == 1);
432 add_channel_command_menu_item (automation_items
, _("Pressure"), MidiChannelPressureAutomation
, 0);
433 automation_items
.back().set_sensitive (!for_selection
|| _editor
.get_selection().tracks
.size() == 1);
435 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
436 since it might need to be updated after a channel mode change or other change. Also detach it
437 first in case it has been used anywhere else.
440 build_controller_menu ();
442 automation_items
.push_back (SeparatorElem());
443 automation_items
.push_back (MenuElem (_("Controllers"), *controller_menu
));
444 automation_items
.back().set_sensitive (!for_selection
|| _editor
.get_selection().tracks
.size() == 1);
446 automation_items
.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
447 dynamic_cast<Label
*> (automation_items
.back().get_child())->set_use_markup (true);
453 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn
, Evoral::Parameter param
)
455 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
457 for (uint8_t chn
= 0; chn
< 16; chn
++) {
458 if (selected_channels
& (0x0001 << chn
)) {
460 Evoral::Parameter
fully_qualified_param (param
.type(), chn
, param
.id());
461 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (fully_qualified_param
);
464 menu
->set_active (yn
);
471 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList
& items
, const string
& label
, AutomationType auto_type
, uint8_t cmd
)
473 using namespace Menu_Helpers
;
475 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
478 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
481 for (uint8_t chn
= 0; chn
< 16; chn
++) {
482 if (selected_channels
& (0x0001 << chn
)) {
491 /* multiple channels - create a submenu, with 1 item per channel */
493 Menu
* chn_menu
= manage (new Menu
);
494 MenuList
& chn_items (chn_menu
->items());
495 Evoral::Parameter
param_without_channel (auto_type
, 0, cmd
);
497 /* add a couple of items to hide/show all of them */
499 chn_items
.push_back (MenuElem (_("Hide all channels"),
500 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility
),
501 false, param_without_channel
)));
502 chn_items
.push_back (MenuElem (_("Show all channels"),
503 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility
),
504 true, param_without_channel
)));
506 for (uint8_t chn
= 0; chn
< 16; chn
++) {
507 if (selected_channels
& (0x0001 << chn
)) {
509 /* for each selected channel, add a menu item for this controller */
511 Evoral::Parameter
fully_qualified_param (auto_type
, chn
, cmd
);
512 chn_items
.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn
+1),
513 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track
),
514 fully_qualified_param
)));
516 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
517 bool visible
= false;
520 if (track
->marked_for_display()) {
525 CheckMenuItem
* cmi
= static_cast<CheckMenuItem
*>(&chn_items
.back());
526 _channel_command_menu_map
[fully_qualified_param
] = cmi
;
527 cmi
->set_active (visible
);
531 /* now create an item in the parent menu that has the per-channel list as a submenu */
533 items
.push_back (MenuElem (label
, *chn_menu
));
537 /* just one channel - create a single menu item for this command+channel combination*/
539 for (uint8_t chn
= 0; chn
< 16; chn
++) {
540 if (selected_channels
& (0x0001 << chn
)) {
542 Evoral::Parameter
fully_qualified_param (auto_type
, chn
, cmd
);
543 items
.push_back (CheckMenuElem (label
,
544 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track
),
545 fully_qualified_param
)));
547 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
548 bool visible
= false;
551 if (track
->marked_for_display()) {
556 CheckMenuItem
* cmi
= static_cast<CheckMenuItem
*>(&items
.back());
557 _channel_command_menu_map
[fully_qualified_param
] = cmi
;
558 cmi
->set_active (visible
);
560 /* one channel only */
568 MidiTimeAxisView::build_controller_menu ()
570 using namespace Menu_Helpers
;
572 if (controller_menu
) {
573 /* it exists and has not been invalidated by a channel mode change, so just return it */
577 controller_menu
= new Menu
; // explicitly managed by us
578 MenuList
& items (controller_menu
->items());
580 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
581 for each controller+channel combination covering the currently selected channels for this track
584 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
586 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
591 for (uint8_t chn
= 0; chn
< 16; chn
++) {
592 if (selected_channels
& (0x0001 << chn
)) {
599 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
600 bank select controllers, as they are handled by the `patch' code */
602 for (int i
= 0; i
< 127; i
+= 16) {
604 Menu
* ctl_menu
= manage (new Menu
);
605 MenuList
& ctl_items (ctl_menu
->items());
608 /* for each controller, consider whether to create a submenu or a single item */
610 for (int ctl
= i
; ctl
< i
+16; ++ctl
) {
612 if (ctl
== MIDI_CTL_MSB_BANK
|| ctl
== MIDI_CTL_LSB_BANK
) {
618 /* multiple channels - create a submenu, with 1 item per channel */
620 Menu
* chn_menu
= manage (new Menu
);
621 MenuList
& chn_items (chn_menu
->items());
623 /* add a couple of items to hide/show this controller on all channels */
625 Evoral::Parameter
param_without_channel (MidiCCAutomation
, 0, ctl
);
626 chn_items
.push_back (MenuElem (_("Hide all channels"),
627 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility
),
628 false, param_without_channel
)));
629 chn_items
.push_back (MenuElem (_("Show all channels"),
630 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility
),
631 true, param_without_channel
)));
633 for (uint8_t chn
= 0; chn
< 16; chn
++) {
634 if (selected_channels
& (0x0001 << chn
)) {
636 /* for each selected channel, add a menu item for this controller */
638 Evoral::Parameter
fully_qualified_param (MidiCCAutomation
, chn
, ctl
);
639 chn_items
.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn
+1),
640 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track
),
641 fully_qualified_param
)));
643 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
644 bool visible
= false;
647 if (track
->marked_for_display()) {
652 CheckMenuItem
* cmi
= static_cast<CheckMenuItem
*>(&chn_items
.back());
653 _controller_menu_map
[fully_qualified_param
] = cmi
;
654 cmi
->set_active (visible
);
658 /* add the per-channel menu to the list of controllers, with the name of the controller */
659 ctl_items
.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl
, midi_name (ctl
)), *chn_menu
));
660 dynamic_cast<Label
*> (ctl_items
.back().get_child())->set_use_markup (true);
664 /* just one channel - create a single menu item for this ctl+channel combination*/
666 for (uint8_t chn
= 0; chn
< 16; chn
++) {
667 if (selected_channels
& (0x0001 << chn
)) {
669 Evoral::Parameter
fully_qualified_param (MidiCCAutomation
, chn
, ctl
);
670 ctl_items
.push_back (
672 string_compose ("<b>%1</b>: %2 [%3]", ctl
, midi_name (ctl
), int (chn
)),
673 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track
),
674 fully_qualified_param
)
677 dynamic_cast<Label
*> (ctl_items
.back().get_child())->set_use_markup (true);
679 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
680 bool visible
= false;
683 if (track
->marked_for_display()) {
688 CheckMenuItem
* cmi
= static_cast<CheckMenuItem
*>(&ctl_items
.back());
689 _controller_menu_map
[fully_qualified_param
] = cmi
;
690 cmi
->set_active (visible
);
692 /* one channel only */
699 /* add the menu for this block of controllers to the overall controller menu */
701 items
.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i
, i
+15), *ctl_menu
));
706 MidiTimeAxisView::build_note_mode_menu()
708 using namespace Menu_Helpers
;
710 Menu
* mode_menu
= manage (new Menu
);
711 MenuList
& items
= mode_menu
->items();
712 mode_menu
->set_name ("ArdourContextMenu");
714 RadioMenuItem::Group mode_group
;
715 items
.push_back (RadioMenuElem (mode_group
, _("Sustained"),
716 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode
), Sustained
)));
717 _note_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
718 _note_mode_item
->set_active(_note_mode
== Sustained
);
720 items
.push_back (RadioMenuElem (mode_group
, _("Percussive"),
721 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode
), Percussive
)));
722 _percussion_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
723 _percussion_mode_item
->set_active(_note_mode
== Percussive
);
729 MidiTimeAxisView::build_color_mode_menu()
731 using namespace Menu_Helpers
;
733 Menu
* mode_menu
= manage (new Menu
);
734 MenuList
& items
= mode_menu
->items();
735 mode_menu
->set_name ("ArdourContextMenu");
737 RadioMenuItem::Group mode_group
;
738 items
.push_back (RadioMenuElem (mode_group
, _("Meter Colors"),
739 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode
),
740 MeterColors
, false, true)));
741 _meter_color_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
742 _meter_color_mode_item
->set_active(_color_mode
== MeterColors
);
744 items
.push_back (RadioMenuElem (mode_group
, _("Channel Colors"),
745 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode
),
746 ChannelColors
, false, true)));
747 _channel_color_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
748 _channel_color_mode_item
->set_active(_color_mode
== ChannelColors
);
750 items
.push_back (RadioMenuElem (mode_group
, _("Track Color"),
751 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode
),
752 TrackColor
, false, true)));
753 _channel_color_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
754 _channel_color_mode_item
->set_active(_color_mode
== TrackColor
);
760 MidiTimeAxisView::set_note_mode(NoteMode mode
)
762 if (_note_mode
!= mode
|| midi_track()->note_mode() != mode
) {
764 midi_track()->set_note_mode(mode
);
765 set_gui_property ("note-mode", enum_2_string(_note_mode
));
766 _view
->redisplay_track();
771 MidiTimeAxisView::set_color_mode (ColorMode mode
, bool force
, bool redisplay
)
773 if (_color_mode
== mode
&& !force
) {
777 if (mode
== ChannelColors
) {
778 _channel_selector
.set_channel_colors(CanvasNoteEvent::midi_channel_colors
);
780 _channel_selector
.set_default_channel_color();
784 set_gui_property ("color-mode", enum_2_string(_color_mode
));
786 _view
->redisplay_track();
791 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range
)
793 if (!_ignore_signals
)
794 midi_view()->set_note_range(range
);
799 MidiTimeAxisView::update_range()
801 MidiGhostRegion
* mgr
;
803 for(list
<GhostRegion
*>::iterator i
= ghosts
.begin(); i
!= ghosts
.end(); ++i
) {
804 if ((mgr
= dynamic_cast<MidiGhostRegion
*>(*i
)) != 0) {
811 MidiTimeAxisView::show_all_automation (bool apply_to_selection
)
813 if (apply_to_selection
) {
814 _editor
.get_selection().tracks
.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation
, _1
, false));
817 const set
<Evoral::Parameter
> params
= midi_track()->midi_playlist()->contained_automation();
819 for (set
<Evoral::Parameter
>::const_iterator i
= params
.begin(); i
!= params
.end(); ++i
) {
820 create_automation_child(*i
, true);
824 RouteTimeAxisView::show_all_automation ();
829 MidiTimeAxisView::show_existing_automation (bool apply_to_selection
)
831 if (apply_to_selection
) {
832 _editor
.get_selection().tracks
.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation
, _1
, false));
835 const set
<Evoral::Parameter
> params
= midi_track()->midi_playlist()->contained_automation();
837 for (set
<Evoral::Parameter
>::const_iterator i
= params
.begin(); i
!= params
.end(); ++i
) {
838 create_automation_child (*i
, true);
842 RouteTimeAxisView::show_existing_automation ();
846 /** Create an automation track for the given parameter (pitch bend, channel pressure).
849 MidiTimeAxisView::create_automation_child (const Evoral::Parameter
& param
, bool show
)
851 if (param
.type() == NullAutomation
) {
852 cerr
<< "WARNING: Attempt to create NullAutomation child, ignoring" << endl
;
856 AutomationTracks::iterator existing
= _automation_tracks
.find (param
);
858 if (existing
!= _automation_tracks
.end()) {
860 /* automation track created because we had existing data for
861 * the processor, but visibility may need to be controlled
862 * since it will have been set visible by default.
865 cerr
<< "show existing auto track: " << show
<< " noredraw " << no_redraw
<< endl
;
867 if (existing
->second
->set_marked_for_display (show
) && !no_redraw
) {
874 boost::shared_ptr
<AutomationTimeAxisView
> track
;
876 switch (param
.type()) {
879 create_gain_automation_child (param
, show
);
882 case PluginAutomation
:
883 /* handled elsewhere */
886 case MidiCCAutomation
:
887 case MidiPgmChangeAutomation
:
888 case MidiPitchBenderAutomation
:
889 case MidiChannelPressureAutomation
:
890 case MidiSystemExclusiveAutomation
:
891 /* These controllers are region "automation" - they are owned
892 * by regions (and their MidiModels), not by the track. As a
893 * result we do not create an AutomationList/Line for the track
894 * ... except here we are doing something!! XXX
897 track
.reset (new AutomationTimeAxisView (
900 boost::shared_ptr
<Automatable
> (),
901 boost::shared_ptr
<AutomationControl
> (),
907 _route
->describe_parameter(param
)
911 _view
->foreach_regionview (sigc::mem_fun (*track
.get(), &TimeAxisView::add_ghost
));
914 add_automation_child (param
, track
, show
);
918 error
<< "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param
) << endmsg
;
923 MidiTimeAxisView::route_active_changed ()
925 RouteUI::route_active_changed ();
928 if (_route
->active()) {
929 controls_ebox
.set_name ("MidiTrackControlsBaseUnselected");
930 controls_base_selected_name
= "MidiTrackControlsBaseSelected";
931 controls_base_unselected_name
= "MidiTrackControlsBaseUnselected";
933 controls_ebox
.set_name ("MidiTrackControlsBaseInactiveUnselected");
934 controls_base_selected_name
= "MidiTrackControlsBaseInactiveSelected";
935 controls_base_unselected_name
= "MidiTrackControlsBaseInactiveUnselected";
941 if (_route
->active()) {
942 controls_ebox
.set_name ("BusControlsBaseUnselected");
943 controls_base_selected_name
= "BusControlsBaseSelected";
944 controls_base_unselected_name
= "BusControlsBaseUnselected";
946 controls_ebox
.set_name ("BusControlsBaseInactiveUnselected");
947 controls_base_selected_name
= "BusControlsBaseInactiveSelected";
948 controls_base_unselected_name
= "BusControlsBaseInactiveUnselected";
956 MidiTimeAxisView::add_note_selection (uint8_t note
)
958 if (!_editor
.internal_editing()) {
962 uint16_t chn_mask
= _channel_selector
.get_selected_channels();
964 if (_view
->num_selected_regionviews() == 0) {
965 _view
->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view
), note
, chn_mask
));
967 _view
->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view
), note
, chn_mask
));
972 MidiTimeAxisView::extend_note_selection (uint8_t note
)
974 if (!_editor
.internal_editing()) {
978 uint16_t chn_mask
= _channel_selector
.get_selected_channels();
980 if (_view
->num_selected_regionviews() == 0) {
981 _view
->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view
), note
, chn_mask
));
983 _view
->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view
), note
, chn_mask
));
988 MidiTimeAxisView::toggle_note_selection (uint8_t note
)
990 if (!_editor
.internal_editing()) {
994 uint16_t chn_mask
= _channel_selector
.get_selected_channels();
996 if (_view
->num_selected_regionviews() == 0) {
997 _view
->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view
), note
, chn_mask
));
999 _view
->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view
), note
, chn_mask
));
1004 MidiTimeAxisView::add_note_selection_region_view (RegionView
* rv
, uint8_t note
, uint16_t chn_mask
)
1006 dynamic_cast<MidiRegionView
*>(rv
)->select_matching_notes (note
, chn_mask
, false, false);
1010 MidiTimeAxisView::extend_note_selection_region_view (RegionView
* rv
, uint8_t note
, uint16_t chn_mask
)
1012 dynamic_cast<MidiRegionView
*>(rv
)->select_matching_notes (note
, chn_mask
, true, true);
1016 MidiTimeAxisView::toggle_note_selection_region_view (RegionView
* rv
, uint8_t note
, uint16_t chn_mask
)
1018 dynamic_cast<MidiRegionView
*>(rv
)->toggle_matching_notes (note
, chn_mask
);
1022 MidiTimeAxisView::set_channel_mode (ChannelMode
, uint16_t)
1024 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1028 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
1029 bool changed
= false;
1033 for (uint32_t ctl
= 0; ctl
< 127; ++ctl
) {
1035 for (uint32_t chn
= 0; chn
< 16; ++chn
) {
1036 Evoral::Parameter
fully_qualified_param (MidiCCAutomation
, chn
, ctl
);
1037 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
1043 if ((selected_channels
& (0x0001 << chn
)) == 0) {
1044 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1045 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1047 changed
= track
->set_marked_for_display (false) || changed
;
1049 changed
= track
->set_marked_for_display (true) || changed
;
1056 /* TODO: Bender, Pressure */
1058 /* invalidate the controller menu, so that we rebuild it next time */
1059 _controller_menu_map
.clear ();
1060 delete controller_menu
;
1061 controller_menu
= 0;
1069 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param
)
1071 Gtk::CheckMenuItem
* m
= RouteTimeAxisView::automation_child_menu_item (param
);
1076 ParameterMenuMap::iterator i
= _controller_menu_map
.find (param
);
1077 if (i
!= _controller_menu_map
.end()) {
1081 i
= _channel_command_menu_map
.find (param
);
1082 if (i
!= _channel_command_menu_map
.end()) {
1089 boost::shared_ptr
<MidiRegion
>
1090 MidiTimeAxisView::add_region (framepos_t pos
, framecnt_t length
, bool commit
)
1092 Editor
* real_editor
= dynamic_cast<Editor
*> (&_editor
);
1094 real_editor
->begin_reversible_command (Operations::create_region
);
1095 playlist()->clear_changes ();
1097 real_editor
->snap_to (pos
, 0);
1099 boost::shared_ptr
<Source
> src
= _session
->create_midi_source_for_session (view()->trackview().track().get(),
1100 view()->trackview().track()->name());
1103 plist
.add (ARDOUR::Properties::start
, 0);
1104 plist
.add (ARDOUR::Properties::length
, length
);
1105 plist
.add (ARDOUR::Properties::name
, PBD::basename_nosuffix(src
->name()));
1107 boost::shared_ptr
<Region
> region
= (RegionFactory::create (src
, plist
));
1109 playlist()->add_region (region
, pos
);
1110 _session
->add_command (new StatefulDiffCommand (playlist()));
1113 real_editor
->commit_reversible_command ();
1116 return boost::dynamic_pointer_cast
<MidiRegion
>(region
);
1120 MidiTimeAxisView::ensure_step_editor ()
1122 if (!_step_editor
) {
1123 _step_editor
= new StepEditor (_editor
, midi_track(), *this);
1128 MidiTimeAxisView::start_step_editing ()
1130 ensure_step_editor ();
1131 _step_editor
->start_step_editing ();
1135 MidiTimeAxisView::stop_step_editing ()
1138 _step_editor
->stop_step_editing ();
1143 /** @return channel (counted from 0) to add an event to, based on the current setting
1144 * of the channel selector.
1147 MidiTimeAxisView::get_channel_for_add () const
1149 uint16_t const chn_mask
= _channel_selector
.get_selected_channels ();
1151 uint8_t channel
= 0;
1153 /* pick the highest selected channel, unless all channels are selected,
1154 which is interpreted to mean channel 1 (zero)
1157 for (uint16_t i
= 0; i
< 16; ++i
) {
1158 if (chn_mask
& (1<<i
)) {
1164 if (chn_cnt
== 16) {
1172 MidiTimeAxisView::note_range_changed ()
1174 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1175 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());