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.
22 #include <strings.h> // for ffs(3)
28 #include <sigc++/bind.h>
30 #include "pbd/error.h"
31 #include "pbd/stl_delete.h"
32 #include "pbd/whitespace.h"
33 #include "pbd/basename.h"
34 #include "pbd/enumwriter.h"
35 #include "pbd/memento_command.h"
36 #include "pbd/stateful_diff_command.h"
38 #include "gtkmm2ext/gtk_ui.h"
39 #include "gtkmm2ext/selector.h"
40 #include "gtkmm2ext/bindable_button.h"
41 #include "gtkmm2ext/utils.h"
43 #include "ardour/event_type_map.h"
44 #include "ardour/midi_patch_manager.h"
45 #include "ardour/midi_playlist.h"
46 #include "ardour/midi_region.h"
47 #include "ardour/midi_source.h"
48 #include "ardour/midi_track.h"
49 #include "ardour/operations.h"
50 #include "ardour/playlist.h"
51 #include "ardour/region.h"
52 #include "ardour/region_factory.h"
53 #include "ardour/route.h"
54 #include "ardour/session.h"
55 #include "ardour/session_object.h"
56 #include "ardour/source.h"
57 #include "ardour/track.h"
58 #include "ardour/types.h"
60 #include "ardour_ui.h"
61 #include "ardour_button.h"
62 #include "automation_line.h"
63 #include "automation_time_axis.h"
66 #include "ghostregion.h"
67 #include "gui_thread.h"
69 #include "midi_channel_selector.h"
70 #include "midi_scroomer.h"
71 #include "midi_streamview.h"
72 #include "midi_region_view.h"
73 #include "midi_time_axis.h"
74 #include "piano_roll_header.h"
75 #include "playlist_selector.h"
76 #include "plugin_selector.h"
77 #include "plugin_ui.h"
78 #include "point_selection.h"
80 #include "region_view.h"
81 #include "rgb_macros.h"
82 #include "selection.h"
83 #include "step_editor.h"
85 #include "note_base.h"
87 #include "ardour/midi_track.h"
91 using namespace ARDOUR;
94 using namespace Gtkmm2ext;
95 using namespace Editing;
97 // Minimum height at which a control is displayed
98 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 140;
99 static const uint32_t KEYBOARD_MIN_HEIGHT = 130;
101 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
102 : AxisView(sess) // virtually inherited
103 , RouteTimeAxisView(ed, sess, canvas)
104 , _ignore_signals(false)
106 , _piano_roll_header(0)
107 , _note_mode(Sustained)
109 , _percussion_mode_item(0)
110 , _color_mode(MeterColors)
111 , _meter_color_mode_item(0)
112 , _channel_color_mode_item(0)
113 , _track_color_mode_item(0)
114 , _channel_selector (0)
115 , _step_edit_item (0)
116 , controller_menu (0)
122 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
126 _view = new MidiStreamView (*this);
129 _piano_roll_header = new PianoRollHeader(*midi_view());
130 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
131 _range_scroomer->DoubleClicked.connect (
132 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
133 MidiStreamView::ContentsRange, false));
136 /* This next call will result in our height being set up, so it must come after
137 the creation of the piano roll / range scroomer as their visibility is set up
140 RouteTimeAxisView::set_route (rt);
142 _view->apply_color (_color, StreamView::RegionColor);
144 subplugin_menu.set_name ("ArdourContextMenu");
146 if (!gui_property ("note-range-min").empty ()) {
147 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
148 atoi (gui_property ("note-range-max").c_str()),
152 midi_view()->NoteRangeChanged.connect (
153 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
154 _view->ContentsHeightChanged.connect (
155 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
157 ignore_toggle = false;
159 if (is_midi_track()) {
160 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
161 _note_mode = midi_track()->note_mode();
162 } else { // MIDI bus (which doesn't exist yet..)
163 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
166 /* map current state of the route */
168 processors_changed (RouteProcessorChange ());
170 _route->processors_changed.connect (*this, invalidator (*this),
171 boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
175 _piano_roll_header->SetNoteSelection.connect (
176 sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
177 _piano_roll_header->AddNoteSelection.connect (
178 sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
179 _piano_roll_header->ExtendNoteSelection.connect (
180 sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
181 _piano_roll_header->ToggleNoteSelection.connect (
182 sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
184 /* Suspend updates of the StreamView during scroomer drags to speed things up */
185 _range_scroomer->DragStarting.connect (
186 sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
187 _range_scroomer->DragFinishing.connect (
188 sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
190 /* Put the scroomer and the keyboard in a VBox with a padding
191 label so that they can be reduced in height for stacked-view
194 VBox* v = manage (new VBox);
195 HBox* h = manage (new HBox);
196 h->pack_start (*_range_scroomer);
197 h->pack_start (*_piano_roll_header);
198 v->pack_start (*h, false, false);
199 v->pack_start (*manage (new Label ("")), true, true);
202 controls_hbox.pack_start(*v, false, false);
204 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
205 controls_base_selected_name = "MidiTrackControlsBaseSelected";
206 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
208 midi_view()->NoteRangeChanged.connect (
209 sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
211 /* ask for notifications of any new RegionViews */
212 _view->RegionViewAdded.connect (
213 sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
215 midi_track()->PlaybackChannelModeChanged.connect (*this, invalidator (*this),
216 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
218 midi_track()->PlaybackChannelMaskChanged.connect (*this, invalidator (*this),
219 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
221 midi_track()->CaptureChannelModeChanged.connect (*this, invalidator (*this),
222 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
224 midi_track()->CaptureChannelMaskChanged.connect (*this, invalidator (*this),
225 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
228 playback_channel_mode_changed ();
229 capture_channel_mode_changed ();
231 if (!_editor.have_idled()) {
232 /* first idle will do what we need */
238 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
240 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
241 for (; m != patch_manager.all_models().end(); ++m) {
242 _midnam_model_selector.append_text(m->c_str());
245 if (gui_property (X_("midnam-model-name")).empty()) {
246 set_gui_property (X_("midnam-model-name"), "Generic");
249 if (gui_property (X_("midnam-custom-device-mode")).empty()) {
250 boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
252 set_gui_property (X_("midnam-custom-device-mode"),
253 *device_names->custom_device_mode_names().begin());
257 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
258 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
260 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
261 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
263 _midi_controls_box.set_homogeneous(false);
264 _midi_controls_box.set_border_width (10);
266 _channel_status_box.set_homogeneous (false);
267 _channel_status_box.set_spacing (6);
269 _channel_selector_button.set_label (_("Chns"));
270 ARDOUR_UI::instance()->set_tip (_channel_selector_button, _("Click to edit channel settings"));
272 /* fixed sized labels to prevent silly nonsense (though obviously,
273 * they cause their own too)
276 _playback_channel_status.set_size_request (65, -1);
277 _capture_channel_status.set_size_request (60, -1);
279 _channel_status_box.pack_start (_playback_channel_status, false, false);
280 _channel_status_box.pack_start (_capture_channel_status, false, false);
281 _channel_status_box.pack_start (_channel_selector_button, false, false);
282 _channel_status_box.show_all ();
284 _channel_selector_button.signal_clicked().connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_channel_selector));
286 _midi_controls_box.pack_start (_channel_status_box, false, false, 10);
288 if (!patch_manager.all_models().empty()) {
290 _midnam_model_selector.set_size_request(22, 30);
291 _midnam_model_selector.set_border_width(2);
292 _midnam_model_selector.show ();
293 _midi_controls_box.pack_start (_midnam_model_selector);
295 _midnam_custom_device_mode_selector.set_size_request(10, 30);
296 _midnam_custom_device_mode_selector.set_border_width(2);
297 _midnam_custom_device_mode_selector.show ();
299 _midi_controls_box.pack_start (_midnam_custom_device_mode_selector);
303 custom_device_mode_changed();
305 _midnam_model_selector.signal_changed().connect(
306 sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
307 _midnam_custom_device_mode_selector.signal_changed().connect(
308 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
310 controls_vbox.pack_start(_midi_controls_box, false, false);
312 const string color_mode = gui_property ("color-mode");
313 if (!color_mode.empty()) {
314 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
315 if (_channel_selector && _color_mode == ChannelColors) {
316 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
320 set_color_mode (_color_mode, true, false);
322 const string note_mode = gui_property ("note-mode");
323 if (!note_mode.empty()) {
324 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
325 if (_percussion_mode_item) {
326 _percussion_mode_item->set_active (_note_mode == Percussive);
330 /* Look for any GUI object state nodes that represent automation children
331 * that should exist, and create the children.
334 const list<string> gui_ids = gui_object_state().all_ids ();
335 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
338 Evoral::Parameter parameter (0, 0, 0);
340 bool const p = AutomationTimeAxisView::parse_state_id (
341 *i, route_id, has_parameter, parameter);
342 if (p && route_id == _route->id () && has_parameter) {
343 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
344 create_automation_child (parameter, string_is_affirmative (visible));
350 MidiTimeAxisView::first_idle ()
357 MidiTimeAxisView::~MidiTimeAxisView ()
359 delete _channel_selector;
361 delete _piano_roll_header;
362 _piano_roll_header = 0;
364 delete _range_scroomer;
367 delete controller_menu;
372 MidiTimeAxisView::enter_internal_edit_mode ()
375 midi_view()->enter_internal_edit_mode ();
380 MidiTimeAxisView::leave_internal_edit_mode ()
383 midi_view()->leave_internal_edit_mode ();
388 MidiTimeAxisView::check_step_edit ()
390 ensure_step_editor ();
391 _step_editor->check_step_edit ();
395 MidiTimeAxisView::model_changed()
397 const Glib::ustring model = _midnam_model_selector.get_active_text();
398 set_gui_property (X_("midnam-model-name"), model);
400 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
401 .custom_device_mode_names_by_model(model);
403 _midnam_custom_device_mode_selector.clear_items();
405 for (std::list<std::string>::const_iterator i = device_modes.begin();
406 i != device_modes.end(); ++i) {
407 _midnam_custom_device_mode_selector.append_text(*i);
410 _midnam_custom_device_mode_selector.set_active(0);
412 _route->instrument_info().set_external_instrument (
413 _midnam_model_selector.get_active_text(),
414 _midnam_custom_device_mode_selector.get_active_text());
416 // Rebuild controller menu
417 _controller_menu_map.clear ();
418 delete controller_menu;
420 build_automation_action_menu(false);
424 MidiTimeAxisView::custom_device_mode_changed()
426 const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text();
427 set_gui_property (X_("midnam-custom-device-mode"), mode);
428 _route->instrument_info().set_external_instrument (
429 _midnam_model_selector.get_active_text(), mode);
433 MidiTimeAxisView::midi_view()
435 return dynamic_cast<MidiStreamView*>(_view);
439 MidiTimeAxisView::set_height (uint32_t h)
441 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
442 _midi_controls_box.show ();
444 _midi_controls_box.hide();
447 if (h >= KEYBOARD_MIN_HEIGHT) {
448 if (is_track() && _range_scroomer) {
449 _range_scroomer->show();
451 if (is_track() && _piano_roll_header) {
452 _piano_roll_header->show();
455 if (is_track() && _range_scroomer) {
456 _range_scroomer->hide();
458 if (is_track() && _piano_roll_header) {
459 _piano_roll_header->hide();
463 /* We need to do this after changing visibility of our stuff, as it will
464 eventually trigger a call to Editor::reset_controls_layout_width(),
465 which needs to know if we have just shown or hidden a scroomer /
468 RouteTimeAxisView::set_height (h);
472 MidiTimeAxisView::append_extra_display_menu_items ()
474 using namespace Menu_Helpers;
476 MenuList& items = display_menu->items();
479 Menu *range_menu = manage(new Menu);
480 MenuList& range_items = range_menu->items();
481 range_menu->set_name ("ArdourContextMenu");
483 range_items.push_back (
484 MenuElem (_("Show Full Range"),
485 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
486 MidiStreamView::FullRange, true)));
488 range_items.push_back (
489 MenuElem (_("Fit Contents"),
490 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
491 MidiStreamView::ContentsRange, true)));
493 items.push_back (MenuElem (_("Note Range"), *range_menu));
494 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
495 items.push_back (MenuElem (_("Channel Selector"),
496 sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector)));
498 color_mode_menu = build_color_mode_menu();
499 if (color_mode_menu) {
500 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
503 items.push_back (SeparatorElem ());
507 MidiTimeAxisView::toggle_channel_selector ()
509 if (!_channel_selector) {
510 _channel_selector = new MidiChannelSelectorWindow (midi_track());
512 if (_color_mode == ChannelColors) {
513 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
515 _channel_selector->set_default_channel_color ();
518 _channel_selector->show_all ();
520 _channel_selector->cycle_visibility ();
525 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
527 using namespace Menu_Helpers;
529 /* If we have a controller menu, we need to detach it before
530 RouteTimeAxis::build_automation_action_menu destroys the
531 menu it is attached to. Otherwise GTK destroys
532 controller_menu's gobj, meaning that it can't be reattached
533 below. See bug #3134.
536 if (controller_menu) {
537 detach_menu (*controller_menu);
540 _channel_command_menu_map.clear ();
541 RouteTimeAxisView::build_automation_action_menu (for_selection);
543 MenuList& automation_items = automation_action_menu->items();
545 uint16_t selected_channels = midi_track()->get_playback_channel_mask();
547 if (selected_channels != 0) {
549 automation_items.push_back (SeparatorElem());
551 /* these 2 MIDI "command" types are semantically more like automation
552 than note data, but they are not MIDI controllers. We give them
553 special status in this menu, since they will not show up in the
554 controller list and anyone who actually knows something about MIDI
555 (!) would not expect to find them there.
558 add_channel_command_menu_item (
559 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
560 automation_items.back().set_sensitive (
561 !for_selection || _editor.get_selection().tracks.size() == 1);
562 add_channel_command_menu_item (
563 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
564 automation_items.back().set_sensitive (
565 !for_selection || _editor.get_selection().tracks.size() == 1);
567 /* now all MIDI controllers. Always offer the possibility that we will
568 rebuild the controllers menu since it might need to be updated after
569 a channel mode change or other change. Also detach it first in case
570 it has been used anywhere else.
573 build_controller_menu ();
575 automation_items.push_back (SeparatorElem());
576 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
577 automation_items.back().set_sensitive (
578 !for_selection || _editor.get_selection().tracks.size() == 1);
580 automation_items.push_back (
581 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
582 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
587 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
589 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
591 for (uint8_t chn = 0; chn < 16; chn++) {
592 if (selected_channels & (0x0001 << chn)) {
594 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
595 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
598 menu->set_active (yn);
605 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
607 AutomationType auto_type,
610 using namespace Menu_Helpers;
612 /* count the number of selected channels because we will build a different menu
613 structure if there is more than 1 selected.
616 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
619 for (uint8_t chn = 0; chn < 16; chn++) {
620 if (selected_channels & (0x0001 << chn)) {
629 /* multiple channels - create a submenu, with 1 item per channel */
631 Menu* chn_menu = manage (new Menu);
632 MenuList& chn_items (chn_menu->items());
633 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
635 /* add a couple of items to hide/show all of them */
637 chn_items.push_back (
638 MenuElem (_("Hide all channels"),
639 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
640 false, param_without_channel)));
641 chn_items.push_back (
642 MenuElem (_("Show all channels"),
643 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
644 true, param_without_channel)));
646 for (uint8_t chn = 0; chn < 16; chn++) {
647 if (selected_channels & (0x0001 << chn)) {
649 /* for each selected channel, add a menu item for this controller */
651 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
652 chn_items.push_back (
653 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
654 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
655 fully_qualified_param)));
657 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
658 bool visible = false;
661 if (track->marked_for_display()) {
666 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
667 _channel_command_menu_map[fully_qualified_param] = cmi;
668 cmi->set_active (visible);
672 /* now create an item in the parent menu that has the per-channel list as a submenu */
674 items.push_back (MenuElem (label, *chn_menu));
678 /* just one channel - create a single menu item for this command+channel combination*/
680 for (uint8_t chn = 0; chn < 16; chn++) {
681 if (selected_channels & (0x0001 << chn)) {
683 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
685 CheckMenuElem (label,
686 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
687 fully_qualified_param)));
689 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
690 bool visible = false;
693 if (track->marked_for_display()) {
698 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
699 _channel_command_menu_map[fully_qualified_param] = cmi;
700 cmi->set_active (visible);
702 /* one channel only */
709 /** Add a single menu item for a controller on one channel. */
711 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
713 const std::string& name)
715 using namespace Menu_Helpers;
717 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
718 for (uint8_t chn = 0; chn < 16; chn++) {
719 if (selected_channels & (0x0001 << chn)) {
721 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
722 ctl_items.push_back (
724 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
726 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
727 fully_qualified_param)));
728 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
730 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
731 fully_qualified_param);
733 bool visible = false;
735 if (track->marked_for_display()) {
740 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
741 _controller_menu_map[fully_qualified_param] = cmi;
742 cmi->set_active (visible);
744 /* one channel only */
750 /** Add a submenu with 1 item per channel for a controller on many channels. */
752 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
754 const std::string& name)
756 using namespace Menu_Helpers;
758 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
760 Menu* chn_menu = manage (new Menu);
761 MenuList& chn_items (chn_menu->items());
763 /* add a couple of items to hide/show this controller on all channels */
765 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
766 chn_items.push_back (
767 MenuElem (_("Hide all channels"),
768 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
769 false, param_without_channel)));
770 chn_items.push_back (
771 MenuElem (_("Show all channels"),
772 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
773 true, param_without_channel)));
775 for (uint8_t chn = 0; chn < 16; chn++) {
776 if (selected_channels & (0x0001 << chn)) {
778 /* for each selected channel, add a menu item for this controller */
780 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
781 chn_items.push_back (
782 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
783 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
784 fully_qualified_param)));
786 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
787 fully_qualified_param);
788 bool visible = false;
791 if (track->marked_for_display()) {
796 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
797 _controller_menu_map[fully_qualified_param] = cmi;
798 cmi->set_active (visible);
802 /* add the per-channel menu to the list of controllers, with the name of the controller */
803 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
805 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
808 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
809 MidiTimeAxisView::get_device_mode()
811 using namespace MIDI::Name;
813 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
815 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
818 return device_names->custom_device_mode_by_name(
819 gui_property (X_("midnam-custom-device-mode")));
822 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
823 MidiTimeAxisView::get_device_names()
825 using namespace MIDI::Name;
827 const std::string model = gui_property (X_("midnam-model-name"));
829 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
830 .document_by_model(model);
832 return midnam->master_device_names(model);
834 return boost::shared_ptr<MasterDeviceNames>();
839 MidiTimeAxisView::build_controller_menu ()
841 using namespace Menu_Helpers;
843 if (controller_menu) {
844 /* it exists and has not been invalidated by a channel mode change */
848 controller_menu = new Menu; // explicitly managed by us
849 MenuList& items (controller_menu->items());
851 /* create several "top level" menu items for sets of controllers (16 at a
852 time), and populate each one with a submenu for each controller+channel
853 combination covering the currently selected channels for this track
856 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
858 /* count the number of selected channels because we will build a different menu
859 structure if there is more than 1 selected.
863 for (uint8_t chn = 0; chn < 16; chn++) {
864 if (selected_channels & (0x0001 << chn)) {
871 using namespace MIDI::Name;
872 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
874 if (device_names && !device_names->controls().empty()) {
875 /* Controllers names available in midnam file, generate fancy menu */
876 unsigned n_items = 0;
877 unsigned n_groups = 0;
879 /* TODO: This is not correct, should look up the currently applicable ControlNameList
880 and only build a menu for that one. */
881 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
882 l != device_names->controls().end(); ++l) {
883 boost::shared_ptr<ControlNameList> name_list = l->second;
884 Menu* ctl_menu = NULL;
886 for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
887 c != name_list->controls().end();) {
888 const uint16_t ctl = c->second->number();
889 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
890 /* Skip bank select controllers since they're handled specially */
892 /* Create a new submenu */
893 ctl_menu = manage (new Menu);
896 MenuList& ctl_items (ctl_menu->items());
898 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
900 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
905 if (ctl_menu && (++n_items == 16 || c == name_list->controls().end())) {
906 /* Submenu has 16 items or we're done, add it to controller menu and reset */
908 MenuElem(string_compose(_("Controllers %1-%2"),
909 (16 * n_groups), (16 * n_groups) + n_items - 1),
918 /* No controllers names, generate generic numeric menu */
919 for (int i = 0; i < 127; i += 16) {
920 Menu* ctl_menu = manage (new Menu);
921 MenuList& ctl_items (ctl_menu->items());
923 for (int ctl = i; ctl < i+16; ++ctl) {
924 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
925 /* Skip bank select controllers since they're handled specially */
930 add_multi_channel_controller_item(
931 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
933 add_single_channel_controller_item(
934 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
938 /* Add submenu for this block of controllers to controller menu */
940 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
947 MidiTimeAxisView::build_note_mode_menu()
949 using namespace Menu_Helpers;
951 Menu* mode_menu = manage (new Menu);
952 MenuList& items = mode_menu->items();
953 mode_menu->set_name ("ArdourContextMenu");
955 RadioMenuItem::Group mode_group;
957 RadioMenuElem (mode_group,_("Sustained"),
958 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
960 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
961 _note_mode_item->set_active(_note_mode == Sustained);
964 RadioMenuElem (mode_group, _("Percussive"),
965 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
967 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
968 _percussion_mode_item->set_active(_note_mode == Percussive);
974 MidiTimeAxisView::build_color_mode_menu()
976 using namespace Menu_Helpers;
978 Menu* mode_menu = manage (new Menu);
979 MenuList& items = mode_menu->items();
980 mode_menu->set_name ("ArdourContextMenu");
982 RadioMenuItem::Group mode_group;
984 RadioMenuElem (mode_group, _("Meter Colors"),
985 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
986 MeterColors, false, true, true)));
987 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
988 _meter_color_mode_item->set_active(_color_mode == MeterColors);
991 RadioMenuElem (mode_group, _("Channel Colors"),
992 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
993 ChannelColors, false, true, true)));
994 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
995 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
998 RadioMenuElem (mode_group, _("Track Color"),
999 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1000 TrackColor, false, true, true)));
1001 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1002 _channel_color_mode_item->set_active(_color_mode == TrackColor);
1008 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
1010 if (apply_to_selection) {
1011 _editor.get_selection().tracks.foreach_midi_time_axis (
1012 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
1014 if (_note_mode != mode || midi_track()->note_mode() != mode) {
1016 midi_track()->set_note_mode(mode);
1017 set_gui_property ("note-mode", enum_2_string(_note_mode));
1018 _view->redisplay_track();
1024 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
1026 if (apply_to_selection) {
1027 _editor.get_selection().tracks.foreach_midi_time_axis (
1028 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
1030 if (_color_mode == mode && !force) {
1034 if (_channel_selector) {
1035 if (mode == ChannelColors) {
1036 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
1038 _channel_selector->set_default_channel_color();
1043 set_gui_property ("color-mode", enum_2_string(_color_mode));
1045 _view->redisplay_track();
1051 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1053 if (apply_to_selection) {
1054 _editor.get_selection().tracks.foreach_midi_time_axis (
1055 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1057 if (!_ignore_signals) {
1058 midi_view()->set_note_range(range);
1064 MidiTimeAxisView::update_range()
1066 MidiGhostRegion* mgr;
1068 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1069 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1070 mgr->update_range();
1076 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1078 if (apply_to_selection) {
1079 _editor.get_selection().tracks.foreach_midi_time_axis (
1080 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1083 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1085 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1086 create_automation_child(*i, true);
1090 RouteTimeAxisView::show_all_automation ();
1095 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1097 if (apply_to_selection) {
1098 _editor.get_selection().tracks.foreach_midi_time_axis (
1099 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1102 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1104 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1105 create_automation_child (*i, true);
1109 RouteTimeAxisView::show_existing_automation ();
1113 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1116 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1118 if (param.type() == NullAutomation) {
1122 AutomationTracks::iterator existing = _automation_tracks.find (param);
1124 if (existing != _automation_tracks.end()) {
1126 /* automation track created because we had existing data for
1127 * the processor, but visibility may need to be controlled
1128 * since it will have been set visible by default.
1131 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1138 boost::shared_ptr<AutomationTimeAxisView> track;
1140 switch (param.type()) {
1142 case GainAutomation:
1143 create_gain_automation_child (param, show);
1146 case PluginAutomation:
1147 /* handled elsewhere */
1150 case MidiCCAutomation:
1151 case MidiPgmChangeAutomation:
1152 case MidiPitchBenderAutomation:
1153 case MidiChannelPressureAutomation:
1154 case MidiSystemExclusiveAutomation:
1155 /* These controllers are region "automation" - they are owned
1156 * by regions (and their MidiModels), not by the track. As a
1157 * result we do not create an AutomationList/Line for the track
1158 * ... except here we are doing something!! XXX
1161 track.reset (new AutomationTimeAxisView (
1164 boost::shared_ptr<Automatable> (),
1165 boost::shared_ptr<AutomationControl> (),
1171 _route->describe_parameter(param)));
1174 _view->foreach_regionview (
1175 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1178 add_automation_child (param, track, show);
1182 error << "MidiTimeAxisView: unknown automation child "
1183 << EventTypeMap::instance().to_symbol(param) << endmsg;
1188 MidiTimeAxisView::route_active_changed ()
1190 RouteUI::route_active_changed ();
1193 if (_route->active()) {
1194 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1195 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1196 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1198 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1199 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1200 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1203 if (_route->active()) {
1204 controls_ebox.set_name ("BusControlsBaseUnselected");
1205 controls_base_selected_name = "BusControlsBaseSelected";
1206 controls_base_unselected_name = "BusControlsBaseUnselected";
1208 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1209 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1210 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1216 MidiTimeAxisView::set_note_selection (uint8_t note)
1218 if (!_editor.internal_editing()) {
1222 uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1224 if (_view->num_selected_regionviews() == 0) {
1225 _view->foreach_regionview (
1226 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1229 _view->foreach_selected_regionview (
1230 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1236 MidiTimeAxisView::add_note_selection (uint8_t note)
1238 if (!_editor.internal_editing()) {
1242 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1244 if (_view->num_selected_regionviews() == 0) {
1245 _view->foreach_regionview (
1246 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1249 _view->foreach_selected_regionview (
1250 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1256 MidiTimeAxisView::extend_note_selection (uint8_t note)
1258 if (!_editor.internal_editing()) {
1262 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1264 if (_view->num_selected_regionviews() == 0) {
1265 _view->foreach_regionview (
1266 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1269 _view->foreach_selected_regionview (
1270 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1276 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1278 if (!_editor.internal_editing()) {
1282 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1284 if (_view->num_selected_regionviews() == 0) {
1285 _view->foreach_regionview (
1286 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1289 _view->foreach_selected_regionview (
1290 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1296 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1298 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1302 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1304 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1308 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1310 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1314 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1316 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1320 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1322 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1326 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1327 bool changed = false;
1331 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1333 for (uint32_t chn = 0; chn < 16; ++chn) {
1334 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1335 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1341 if ((selected_channels & (0x0001 << chn)) == 0) {
1342 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1343 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1345 changed = track->set_marked_for_display (false) || changed;
1347 changed = track->set_marked_for_display (true) || changed;
1354 /* TODO: Bender, Pressure */
1356 /* invalidate the controller menu, so that we rebuild it next time */
1357 _controller_menu_map.clear ();
1358 delete controller_menu;
1359 controller_menu = 0;
1367 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1369 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1374 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1375 if (i != _controller_menu_map.end()) {
1379 i = _channel_command_menu_map.find (param);
1380 if (i != _channel_command_menu_map.end()) {
1387 boost::shared_ptr<MidiRegion>
1388 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1390 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1392 real_editor->begin_reversible_command (Operations::create_region);
1393 playlist()->clear_changes ();
1395 real_editor->snap_to (pos, 0);
1397 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1398 view()->trackview().track().get(), view()->trackview().track()->name());
1401 plist.add (ARDOUR::Properties::start, 0);
1402 plist.add (ARDOUR::Properties::length, length);
1403 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1405 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1407 playlist()->add_region (region, pos);
1408 _session->add_command (new StatefulDiffCommand (playlist()));
1411 real_editor->commit_reversible_command ();
1414 return boost::dynamic_pointer_cast<MidiRegion>(region);
1418 MidiTimeAxisView::ensure_step_editor ()
1420 if (!_step_editor) {
1421 _step_editor = new StepEditor (_editor, midi_track(), *this);
1426 MidiTimeAxisView::start_step_editing ()
1428 ensure_step_editor ();
1429 _step_editor->start_step_editing ();
1433 MidiTimeAxisView::stop_step_editing ()
1436 _step_editor->stop_step_editing ();
1440 /** @return channel (counted from 0) to add an event to, based on the current setting
1441 * of the channel selector.
1444 MidiTimeAxisView::get_channel_for_add () const
1446 uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
1448 uint8_t channel = 0;
1450 /* pick the highest selected channel, unless all channels are selected,
1451 which is interpreted to mean channel 1 (zero)
1454 for (uint16_t i = 0; i < 16; ++i) {
1455 if (chn_mask & (1<<i)) {
1461 if (chn_cnt == 16) {
1469 MidiTimeAxisView::note_range_changed ()
1471 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1472 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1476 MidiTimeAxisView::contents_height_changed ()
1478 _range_scroomer->set_size_request (-1, _view->child_height ());
1482 MidiTimeAxisView::playback_channel_mode_changed ()
1484 switch (midi_track()->get_playback_channel_mode()) {
1486 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("all")));
1488 case FilterChannels:
1489 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("some")));
1492 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Play"), _("all"), ffs (midi_track()->get_playback_channel_mask())));
1498 MidiTimeAxisView::capture_channel_mode_changed ()
1500 switch (midi_track()->get_capture_channel_mode()) {
1502 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("all")));
1504 case FilterChannels:
1505 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("some")));
1508 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Rec"), _("all"), ffs (midi_track()->get_capture_channel_mask())));