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"
30 #include "pbd/stl_delete.h"
31 #include "pbd/whitespace.h"
32 #include "pbd/basename.h"
33 #include "pbd/enumwriter.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/stateful_diff_command.h"
37 #include "gtkmm2ext/gtk_ui.h"
38 #include "gtkmm2ext/selector.h"
39 #include "gtkmm2ext/bindable_button.h"
40 #include "gtkmm2ext/utils.h"
42 #include "ardour/event_type_map.h"
43 #include "ardour/midi_patch_manager.h"
44 #include "ardour/midi_playlist.h"
45 #include "ardour/midi_region.h"
46 #include "ardour/midi_source.h"
47 #include "ardour/midi_track.h"
48 #include "ardour/operations.h"
49 #include "ardour/pannable.h"
50 #include "ardour/panner.h"
51 #include "ardour/panner_shell.h"
52 #include "ardour/playlist.h"
53 #include "ardour/profile.h"
54 #include "ardour/region.h"
55 #include "ardour/region_factory.h"
56 #include "ardour/route.h"
57 #include "ardour/session.h"
58 #include "ardour/session_object.h"
59 #include "ardour/source.h"
60 #include "ardour/track.h"
61 #include "ardour/types.h"
63 #include "ardour_ui.h"
64 #include "ardour_button.h"
65 #include "automation_line.h"
66 #include "automation_time_axis.h"
69 #include "ghostregion.h"
70 #include "gui_thread.h"
72 #include "midi_channel_selector.h"
73 #include "midi_scroomer.h"
74 #include "midi_streamview.h"
75 #include "midi_region_view.h"
76 #include "midi_time_axis.h"
77 #include "piano_roll_header.h"
78 #include "playlist_selector.h"
79 #include "plugin_selector.h"
80 #include "plugin_ui.h"
81 #include "point_selection.h"
83 #include "region_view.h"
84 #include "rgb_macros.h"
85 #include "selection.h"
86 #include "step_editor.h"
88 #include "note_base.h"
90 #include "ardour/midi_track.h"
94 using namespace ARDOUR;
95 using namespace ARDOUR_UI_UTILS;
98 using namespace Gtkmm2ext;
99 using namespace Editing;
102 // Minimum height at which a control is displayed
103 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 160;
104 static const uint32_t KEYBOARD_MIN_HEIGHT = 130;
106 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
107 : AxisView(sess) // virtually inherited
108 , RouteTimeAxisView(ed, sess, canvas)
109 , _ignore_signals(false)
111 , _piano_roll_header(0)
112 , _note_mode(Sustained)
114 , _percussion_mode_item(0)
115 , _color_mode(MeterColors)
116 , _meter_color_mode_item(0)
117 , _channel_color_mode_item(0)
118 , _track_color_mode_item(0)
119 , _channel_selector (0)
120 , _step_edit_item (0)
121 , controller_menu (0)
127 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
131 _view = new MidiStreamView (*this);
134 _piano_roll_header = new PianoRollHeader(*midi_view());
135 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
136 _range_scroomer->DoubleClicked.connect (
137 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
138 MidiStreamView::ContentsRange, false));
141 /* This next call will result in our height being set up, so it must come after
142 the creation of the piano roll / range scroomer as their visibility is set up
145 RouteTimeAxisView::set_route (rt);
147 _view->apply_color (_color, StreamView::RegionColor);
149 subplugin_menu.set_name ("ArdourContextMenu");
151 if (!gui_property ("note-range-min").empty ()) {
152 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
153 atoi (gui_property ("note-range-max").c_str()),
157 midi_view()->NoteRangeChanged.connect (
158 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
159 _view->ContentsHeightChanged.connect (
160 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
162 ignore_toggle = false;
164 if (is_midi_track()) {
165 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
166 time_axis_frame.set_name ("MidiTimeAxisViewControlsBaseUnselected");
167 _note_mode = midi_track()->note_mode();
168 } else { // MIDI bus (which doesn't exist yet..)
169 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
170 time_axis_frame.set_name ("MidiBusControlsBaseUnselected");
173 /* if set_state above didn't create a gain automation child, we need to make one */
174 if (automation_child (GainAutomation) == 0) {
175 create_automation_child (GainAutomation, false);
178 /* if set_state above didn't create a mute automation child, we need to make one */
179 if (automation_child (MuteAutomation) == 0) {
180 create_automation_child (MuteAutomation, false);
183 if (_route->panner_shell()) {
184 _route->panner_shell()->Changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::ensure_pan_views, this, false), gui_context());
187 /* map current state of the route */
188 ensure_pan_views (false);
190 processors_changed (RouteProcessorChange ());
192 _route->processors_changed.connect (*this, invalidator (*this),
193 boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
197 _piano_roll_header->SetNoteSelection.connect (
198 sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
199 _piano_roll_header->AddNoteSelection.connect (
200 sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
201 _piano_roll_header->ExtendNoteSelection.connect (
202 sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
203 _piano_roll_header->ToggleNoteSelection.connect (
204 sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
206 /* Suspend updates of the StreamView during scroomer drags to speed things up */
207 _range_scroomer->DragStarting.connect (
208 sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
209 _range_scroomer->DragFinishing.connect (
210 sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
212 /* Put the scroomer and the keyboard in a VBox with a padding
213 label so that they can be reduced in height for stacked-view
217 HSeparator* separator = manage (new HSeparator());
218 separator->set_name("TrackSeparator");
219 separator->set_size_request(-1, 1);
222 VBox* v = manage (new VBox);
223 HBox* h = manage (new HBox);
224 h->pack_end (*_piano_roll_header);
225 h->pack_end (*_range_scroomer);
226 v->pack_start (*separator, false, false);
227 v->pack_start (*h, true, true);
230 top_hbox.remove(scroomer_placeholder);
231 time_axis_hbox.pack_end(*v, false, false, 0);
232 midi_scroomer_size_group->add_widget (*v);
234 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
235 time_axis_frame.set_name ("MidiTrackControlsBaseUnselected");
236 controls_base_selected_name = "MidiTrackControlsBaseSelected";
237 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
239 midi_view()->NoteRangeChanged.connect (
240 sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
242 /* ask for notifications of any new RegionViews */
243 _view->RegionViewAdded.connect (
244 sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
246 midi_track()->PlaybackChannelModeChanged.connect (*this, invalidator (*this),
247 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
249 midi_track()->PlaybackChannelMaskChanged.connect (*this, invalidator (*this),
250 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
252 midi_track()->CaptureChannelModeChanged.connect (*this, invalidator (*this),
253 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
255 midi_track()->CaptureChannelMaskChanged.connect (*this, invalidator (*this),
256 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
259 playback_channel_mode_changed ();
260 capture_channel_mode_changed ();
262 if (!_editor.have_idled()) {
263 /* first idle will do what we need */
269 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
271 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
272 for (; m != patch_manager.all_models().end(); ++m) {
273 _midnam_model_selector.AddMenuElem(
274 Gtk::Menu_Helpers::MenuElem(m->c_str(),
275 sigc::bind(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed),
279 if (gui_property (X_("midnam-model-name")).empty()) {
280 set_gui_property (X_("midnam-model-name"), "Generic");
283 if (gui_property (X_("midnam-custom-device-mode")).empty()) {
284 boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
286 set_gui_property (X_("midnam-custom-device-mode"),
287 *device_names->custom_device_mode_names().begin());
291 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
292 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
294 _midi_controls_box.set_homogeneous(false);
295 _midi_controls_box.set_border_width (2);
297 _channel_status_box.set_homogeneous (false);
298 _channel_status_box.set_spacing (4);
300 ArdourButton *channel_selector_button = manage (new ArdourButton(_("Chns")));
301 channel_selector_button->set_name ("route button");
302 ARDOUR_UI::instance()->set_tip (channel_selector_button, _("Click to edit channel settings"));
304 /* fixed sized labels to prevent silly nonsense (though obviously,
305 * they cause their own too)
307 set_size_request_to_display_given_text(_playback_channel_status, "Play: somemo", 2, 2); // TODO use _("Play: all/some")
308 set_size_request_to_display_given_text(_capture_channel_status, "Rec: somemo", 2, 2); // TODO use _("Rec: all/some")
310 _channel_status_box.pack_start (_playback_channel_status, false, false);
311 _channel_status_box.pack_start (_capture_channel_status, false, false);
312 _channel_status_box.pack_end (*channel_selector_button, false, false);
313 _channel_status_box.show_all ();
315 channel_selector_button->signal_clicked.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_channel_selector));
317 _midi_controls_box.pack_start (_channel_status_box, false, false, 10);
319 if (!patch_manager.all_models().empty()) {
321 _midnam_model_selector.show ();
322 _midi_controls_box.pack_start (_midnam_model_selector, false, false, 2);
324 _midnam_custom_device_mode_selector.show ();
326 _midi_controls_box.pack_start (_midnam_custom_device_mode_selector, false, false, 2);
329 model_changed(gui_property(X_("midnam-model-name")));
330 custom_device_mode_changed(gui_property(X_("midnam-custom-device-mode")));
332 controls_vbox.pack_start(_midi_controls_box, false, false);
334 const string color_mode = gui_property ("color-mode");
335 if (!color_mode.empty()) {
336 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
337 if (_channel_selector && _color_mode == ChannelColors) {
338 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
342 set_color_mode (_color_mode, true, false);
344 const string note_mode = gui_property ("note-mode");
345 if (!note_mode.empty()) {
346 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
347 if (_percussion_mode_item) {
348 _percussion_mode_item->set_active (_note_mode == Percussive);
352 /* Look for any GUI object state nodes that represent automation children
353 * that should exist, and create the children.
356 const list<string> gui_ids = gui_object_state().all_ids ();
357 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
360 Evoral::Parameter parameter (0, 0, 0);
362 bool const p = AutomationTimeAxisView::parse_state_id (
363 *i, route_id, has_parameter, parameter);
364 if (p && route_id == _route->id () && has_parameter) {
365 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
366 create_automation_child (parameter, string_is_affirmative (visible));
372 MidiTimeAxisView::first_idle ()
379 MidiTimeAxisView::~MidiTimeAxisView ()
381 delete _channel_selector;
383 delete _piano_roll_header;
384 _piano_roll_header = 0;
386 delete _range_scroomer;
389 delete controller_menu;
394 MidiTimeAxisView::enter_internal_edit_mode ()
397 midi_view()->enter_internal_edit_mode ();
402 MidiTimeAxisView::leave_internal_edit_mode ()
405 midi_view()->leave_internal_edit_mode ();
410 MidiTimeAxisView::check_step_edit ()
412 ensure_step_editor ();
413 _step_editor->check_step_edit ();
417 MidiTimeAxisView::model_changed(const std::string& model)
419 set_gui_property (X_("midnam-model-name"), model);
421 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
422 .custom_device_mode_names_by_model(model);
424 _midnam_model_selector.set_text(model);
425 _midnam_custom_device_mode_selector.clear_items();
427 for (std::list<std::string>::const_iterator i = device_modes.begin();
428 i != device_modes.end(); ++i) {
429 _midnam_custom_device_mode_selector.AddMenuElem(
430 Gtk::Menu_Helpers::MenuElem(
431 *i, sigc::bind(sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed),
435 if (!device_modes.empty()) {
436 custom_device_mode_changed(device_modes.front());
439 if (device_modes.size() > 1) {
440 _midnam_custom_device_mode_selector.show();
442 _midnam_custom_device_mode_selector.hide();
445 _route->instrument_info().set_external_instrument (model, device_modes.front());
447 // Rebuild controller menu
448 _controller_menu_map.clear ();
449 delete controller_menu;
451 build_automation_action_menu(false);
455 MidiTimeAxisView::custom_device_mode_changed(const std::string& mode)
457 const std::string model = gui_property (X_("midnam-model-name"));
459 set_gui_property (X_("midnam-custom-device-mode"), mode);
460 _midnam_custom_device_mode_selector.set_text(mode);
461 _route->instrument_info().set_external_instrument (model, mode);
465 MidiTimeAxisView::midi_view()
467 return dynamic_cast<MidiStreamView*>(_view);
471 MidiTimeAxisView::set_height (uint32_t h)
473 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
474 _midi_controls_box.show ();
476 _midi_controls_box.hide();
479 if (h >= KEYBOARD_MIN_HEIGHT) {
480 if (is_track() && _range_scroomer) {
481 _range_scroomer->show();
483 if (is_track() && _piano_roll_header) {
484 _piano_roll_header->show();
487 if (is_track() && _range_scroomer) {
488 _range_scroomer->hide();
490 if (is_track() && _piano_roll_header) {
491 _piano_roll_header->hide();
495 /* We need to do this after changing visibility of our stuff, as it will
496 eventually trigger a call to Editor::reset_controls_layout_width(),
497 which needs to know if we have just shown or hidden a scroomer /
500 RouteTimeAxisView::set_height (h);
504 MidiTimeAxisView::append_extra_display_menu_items ()
506 using namespace Menu_Helpers;
508 MenuList& items = display_menu->items();
511 Menu *range_menu = manage(new Menu);
512 MenuList& range_items = range_menu->items();
513 range_menu->set_name ("ArdourContextMenu");
515 range_items.push_back (
516 MenuElem (_("Show Full Range"),
517 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
518 MidiStreamView::FullRange, true)));
520 range_items.push_back (
521 MenuElem (_("Fit Contents"),
522 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
523 MidiStreamView::ContentsRange, true)));
525 items.push_back (MenuElem (_("Note Range"), *range_menu));
526 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
527 items.push_back (MenuElem (_("Channel Selector"),
528 sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector)));
530 color_mode_menu = build_color_mode_menu();
531 if (color_mode_menu) {
532 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
535 items.push_back (SeparatorElem ());
539 MidiTimeAxisView::toggle_channel_selector ()
541 if (!_channel_selector) {
542 _channel_selector = new MidiChannelSelectorWindow (midi_track());
544 if (_color_mode == ChannelColors) {
545 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
547 _channel_selector->set_default_channel_color ();
550 _channel_selector->show_all ();
552 _channel_selector->cycle_visibility ();
557 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
559 using namespace Menu_Helpers;
561 /* If we have a controller menu, we need to detach it before
562 RouteTimeAxis::build_automation_action_menu destroys the
563 menu it is attached to. Otherwise GTK destroys
564 controller_menu's gobj, meaning that it can't be reattached
565 below. See bug #3134.
568 if (controller_menu) {
569 detach_menu (*controller_menu);
572 _channel_command_menu_map.clear ();
573 RouteTimeAxisView::build_automation_action_menu (for_selection);
575 MenuList& automation_items = automation_action_menu->items();
577 uint16_t selected_channels = midi_track()->get_playback_channel_mask();
579 if (selected_channels != 0) {
581 automation_items.push_back (SeparatorElem());
583 /* these 2 MIDI "command" types are semantically more like automation
584 than note data, but they are not MIDI controllers. We give them
585 special status in this menu, since they will not show up in the
586 controller list and anyone who actually knows something about MIDI
587 (!) would not expect to find them there.
590 add_channel_command_menu_item (
591 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
592 automation_items.back().set_sensitive (
593 !for_selection || _editor.get_selection().tracks.size() == 1);
594 add_channel_command_menu_item (
595 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
596 automation_items.back().set_sensitive (
597 !for_selection || _editor.get_selection().tracks.size() == 1);
599 /* now all MIDI controllers. Always offer the possibility that we will
600 rebuild the controllers menu since it might need to be updated after
601 a channel mode change or other change. Also detach it first in case
602 it has been used anywhere else.
605 build_controller_menu ();
607 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
608 automation_items.back().set_sensitive (
609 !for_selection || _editor.get_selection().tracks.size() == 1);
611 automation_items.push_back (
612 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
613 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
618 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
620 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
622 for (uint8_t chn = 0; chn < 16; chn++) {
623 if (selected_channels & (0x0001 << chn)) {
625 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
626 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
629 menu->set_active (yn);
636 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
638 AutomationType auto_type,
641 using namespace Menu_Helpers;
643 /* count the number of selected channels because we will build a different menu
644 structure if there is more than 1 selected.
647 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
650 for (uint8_t chn = 0; chn < 16; chn++) {
651 if (selected_channels & (0x0001 << chn)) {
660 /* multiple channels - create a submenu, with 1 item per channel */
662 Menu* chn_menu = manage (new Menu);
663 MenuList& chn_items (chn_menu->items());
664 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
666 /* add a couple of items to hide/show all of them */
668 chn_items.push_back (
669 MenuElem (_("Hide all channels"),
670 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
671 false, param_without_channel)));
672 chn_items.push_back (
673 MenuElem (_("Show all channels"),
674 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
675 true, param_without_channel)));
677 for (uint8_t chn = 0; chn < 16; chn++) {
678 if (selected_channels & (0x0001 << chn)) {
680 /* for each selected channel, add a menu item for this controller */
682 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
683 chn_items.push_back (
684 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
685 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
686 fully_qualified_param)));
688 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
689 bool visible = false;
692 if (track->marked_for_display()) {
697 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
698 _channel_command_menu_map[fully_qualified_param] = cmi;
699 cmi->set_active (visible);
703 /* now create an item in the parent menu that has the per-channel list as a submenu */
705 items.push_back (MenuElem (label, *chn_menu));
709 /* just one channel - create a single menu item for this command+channel combination*/
711 for (uint8_t chn = 0; chn < 16; chn++) {
712 if (selected_channels & (0x0001 << chn)) {
714 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
716 CheckMenuElem (label,
717 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
718 fully_qualified_param)));
720 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
721 bool visible = false;
724 if (track->marked_for_display()) {
729 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&items.back());
730 _channel_command_menu_map[fully_qualified_param] = cmi;
731 cmi->set_active (visible);
733 /* one channel only */
740 /** Add a single menu item for a controller on one channel. */
742 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
744 const std::string& name)
746 using namespace Menu_Helpers;
748 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
749 for (uint8_t chn = 0; chn < 16; chn++) {
750 if (selected_channels & (0x0001 << chn)) {
752 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
753 ctl_items.push_back (
755 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn + 1)),
757 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
758 fully_qualified_param)));
759 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
761 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
762 fully_qualified_param);
764 bool visible = false;
766 if (track->marked_for_display()) {
771 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&ctl_items.back());
772 _controller_menu_map[fully_qualified_param] = cmi;
773 cmi->set_active (visible);
775 /* one channel only */
781 /** Add a submenu with 1 item per channel for a controller on many channels. */
783 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
785 const std::string& name)
787 using namespace Menu_Helpers;
789 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
791 Menu* chn_menu = manage (new Menu);
792 MenuList& chn_items (chn_menu->items());
794 /* add a couple of items to hide/show this controller on all channels */
796 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
797 chn_items.push_back (
798 MenuElem (_("Hide all channels"),
799 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
800 false, param_without_channel)));
801 chn_items.push_back (
802 MenuElem (_("Show all channels"),
803 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
804 true, param_without_channel)));
806 for (uint8_t chn = 0; chn < 16; chn++) {
807 if (selected_channels & (0x0001 << chn)) {
809 /* for each selected channel, add a menu item for this controller */
811 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
812 chn_items.push_back (
813 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
814 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
815 fully_qualified_param)));
817 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
818 fully_qualified_param);
819 bool visible = false;
822 if (track->marked_for_display()) {
827 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
828 _controller_menu_map[fully_qualified_param] = cmi;
829 cmi->set_active (visible);
833 /* add the per-channel menu to the list of controllers, with the name of the controller */
834 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
836 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
839 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
840 MidiTimeAxisView::get_device_mode()
842 using namespace MIDI::Name;
844 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
846 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
849 return device_names->custom_device_mode_by_name(
850 gui_property (X_("midnam-custom-device-mode")));
853 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
854 MidiTimeAxisView::get_device_names()
856 using namespace MIDI::Name;
858 const std::string model = gui_property (X_("midnam-model-name"));
860 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
861 .document_by_model(model);
863 return midnam->master_device_names(model);
865 return boost::shared_ptr<MasterDeviceNames>();
870 MidiTimeAxisView::build_controller_menu ()
872 using namespace Menu_Helpers;
874 if (controller_menu) {
875 /* it exists and has not been invalidated by a channel mode change */
879 controller_menu = new Menu; // explicitly managed by us
880 MenuList& items (controller_menu->items());
882 /* create several "top level" menu items for sets of controllers (16 at a
883 time), and populate each one with a submenu for each controller+channel
884 combination covering the currently selected channels for this track
887 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
889 /* count the number of selected channels because we will build a different menu
890 structure if there is more than 1 selected.
894 for (uint8_t chn = 0; chn < 16; chn++) {
895 if (selected_channels & (0x0001 << chn)) {
902 using namespace MIDI::Name;
903 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
905 if (device_names && !device_names->controls().empty()) {
906 /* Controllers names available in midnam file, generate fancy menu */
907 unsigned n_items = 0;
908 unsigned n_groups = 0;
910 /* TODO: This is not correct, should look up the currently applicable ControlNameList
911 and only build a menu for that one. */
912 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
913 l != device_names->controls().end(); ++l) {
914 boost::shared_ptr<ControlNameList> name_list = l->second;
915 Menu* ctl_menu = NULL;
917 for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
918 c != name_list->controls().end();) {
919 const uint16_t ctl = c->second->number();
920 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
921 /* Skip bank select controllers since they're handled specially */
923 /* Create a new submenu */
924 ctl_menu = manage (new Menu);
927 MenuList& ctl_items (ctl_menu->items());
929 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
931 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
936 if (ctl_menu && (++n_items == 16 || c == name_list->controls().end())) {
937 /* Submenu has 16 items or we're done, add it to controller menu and reset */
939 MenuElem(string_compose(_("Controllers %1-%2"),
940 (16 * n_groups), (16 * n_groups) + n_items - 1),
949 /* No controllers names, generate generic numeric menu */
950 for (int i = 0; i < 127; i += 16) {
951 Menu* ctl_menu = manage (new Menu);
952 MenuList& ctl_items (ctl_menu->items());
954 for (int ctl = i; ctl < i+16; ++ctl) {
955 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
956 /* Skip bank select controllers since they're handled specially */
961 add_multi_channel_controller_item(
962 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
964 add_single_channel_controller_item(
965 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
969 /* Add submenu for this block of controllers to controller menu */
971 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
978 MidiTimeAxisView::build_note_mode_menu()
980 using namespace Menu_Helpers;
982 Menu* mode_menu = manage (new Menu);
983 MenuList& items = mode_menu->items();
984 mode_menu->set_name ("ArdourContextMenu");
986 RadioMenuItem::Group mode_group;
988 RadioMenuElem (mode_group,_("Sustained"),
989 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
991 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
992 _note_mode_item->set_active(_note_mode == Sustained);
995 RadioMenuElem (mode_group, _("Percussive"),
996 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
998 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
999 _percussion_mode_item->set_active(_note_mode == Percussive);
1005 MidiTimeAxisView::build_color_mode_menu()
1007 using namespace Menu_Helpers;
1009 Menu* mode_menu = manage (new Menu);
1010 MenuList& items = mode_menu->items();
1011 mode_menu->set_name ("ArdourContextMenu");
1013 RadioMenuItem::Group mode_group;
1015 RadioMenuElem (mode_group, _("Meter Colors"),
1016 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1017 MeterColors, false, true, true)));
1018 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1019 _meter_color_mode_item->set_active(_color_mode == MeterColors);
1022 RadioMenuElem (mode_group, _("Channel Colors"),
1023 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1024 ChannelColors, false, true, true)));
1025 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1026 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
1029 RadioMenuElem (mode_group, _("Track Color"),
1030 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1031 TrackColor, false, true, true)));
1032 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1033 _channel_color_mode_item->set_active(_color_mode == TrackColor);
1039 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
1041 if (apply_to_selection) {
1042 _editor.get_selection().tracks.foreach_midi_time_axis (
1043 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
1045 if (_note_mode != mode || midi_track()->note_mode() != mode) {
1047 midi_track()->set_note_mode(mode);
1048 set_gui_property ("note-mode", enum_2_string(_note_mode));
1049 _view->redisplay_track();
1055 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
1057 if (apply_to_selection) {
1058 _editor.get_selection().tracks.foreach_midi_time_axis (
1059 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
1061 if (_color_mode == mode && !force) {
1065 if (_channel_selector) {
1066 if (mode == ChannelColors) {
1067 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
1069 _channel_selector->set_default_channel_color();
1074 set_gui_property ("color-mode", enum_2_string(_color_mode));
1076 _view->redisplay_track();
1082 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1084 if (apply_to_selection) {
1085 _editor.get_selection().tracks.foreach_midi_time_axis (
1086 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1088 if (!_ignore_signals) {
1089 midi_view()->set_note_range(range);
1095 MidiTimeAxisView::update_range()
1097 MidiGhostRegion* mgr;
1099 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1100 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1101 mgr->update_range();
1107 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1109 using namespace MIDI::Name;
1111 if (apply_to_selection) {
1112 _editor.get_selection().tracks.foreach_midi_time_axis (
1113 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1116 // Show existing automation
1117 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1119 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1120 create_automation_child(*i, true);
1123 // Show automation for all controllers named in midnam file
1124 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
1125 if (gui_property (X_("midnam-model-name")) != "Generic" &&
1126 device_names && !device_names->controls().empty()) {
1127 const std::string device_mode = gui_property (X_("midnam-custom-device-mode"));
1128 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1129 for (uint32_t chn = 0; chn < 16; ++chn) {
1130 if ((selected_channels & (0x0001 << chn)) == 0) {
1131 // Channel not in use
1135 boost::shared_ptr<ChannelNameSet> chan_names = device_names->channel_name_set_by_channel(
1141 boost::shared_ptr<ControlNameList> control_names = device_names->control_name_list(
1142 chan_names->control_list_name());
1143 if (!control_names) {
1147 for (ControlNameList::Controls::const_iterator c = control_names->controls().begin();
1148 c != control_names->controls().end();
1150 const uint16_t ctl = c->second->number();
1151 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
1152 /* Skip bank select controllers since they're handled specially */
1153 const Evoral::Parameter param(MidiCCAutomation, chn, ctl);
1154 create_automation_child(param, true);
1161 RouteTimeAxisView::show_all_automation ();
1166 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1168 if (apply_to_selection) {
1169 _editor.get_selection().tracks.foreach_midi_time_axis (
1170 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1173 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1175 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1176 create_automation_child (*i, true);
1180 RouteTimeAxisView::show_existing_automation ();
1184 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1187 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1189 if (param.type() == NullAutomation) {
1193 AutomationTracks::iterator existing = _automation_tracks.find (param);
1195 if (existing != _automation_tracks.end()) {
1197 /* automation track created because we had existing data for
1198 * the processor, but visibility may need to be controlled
1199 * since it will have been set visible by default.
1202 existing->second->set_marked_for_display (show);
1211 boost::shared_ptr<AutomationTimeAxisView> track;
1212 boost::shared_ptr<AutomationControl> control;
1215 switch (param.type()) {
1217 case GainAutomation:
1218 create_gain_automation_child (param, show);
1221 case MuteAutomation:
1222 create_mute_automation_child (param, show);
1225 case PluginAutomation:
1226 /* handled elsewhere */
1229 case MidiCCAutomation:
1230 case MidiPgmChangeAutomation:
1231 case MidiPitchBenderAutomation:
1232 case MidiChannelPressureAutomation:
1233 case MidiSystemExclusiveAutomation:
1234 /* These controllers are region "automation" - they are owned
1235 * by regions (and their MidiModels), not by the track. As a
1236 * result there is no AutomationList/Line for the track, but we create
1237 * a controller for the user to write immediate events, so the editor
1238 * can act as a control surface for the present MIDI controllers.
1240 * TODO: Record manipulation of the controller to regions?
1243 control = _route->automation_control(param, true);
1244 track.reset (new AutomationTimeAxisView (
1247 control ? _route : boost::shared_ptr<Automatable> (),
1254 _route->describe_parameter(param)));
1257 _view->foreach_regionview (
1258 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1261 add_automation_child (param, track, show);
1264 case PanWidthAutomation:
1265 case PanElevationAutomation:
1266 case PanAzimuthAutomation:
1267 ensure_pan_views (show);
1271 error << "MidiTimeAxisView: unknown automation child "
1272 << EventTypeMap::instance().to_symbol(param) << endmsg;
1277 MidiTimeAxisView::route_active_changed ()
1279 RouteUI::route_active_changed ();
1282 if (_route->active()) {
1283 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1284 time_axis_frame.set_name ("MidiTrackControlsBaseUnselected");
1285 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1286 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1288 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1289 time_axis_frame.set_name ("MidiTrackControlsBaseInactiveUnselected");
1290 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1291 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1294 if (_route->active()) {
1295 controls_ebox.set_name ("BusControlsBaseUnselected");
1296 time_axis_frame.set_name ("BusControlsBaseUnselected");
1297 controls_base_selected_name = "BusControlsBaseSelected";
1298 controls_base_unselected_name = "BusControlsBaseUnselected";
1300 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1301 time_axis_frame.set_name ("BusControlsBaseInactiveUnselected");
1302 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1303 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1309 MidiTimeAxisView::set_note_selection (uint8_t note)
1311 if (!_editor.internal_editing()) {
1315 uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1317 if (_view->num_selected_regionviews() == 0) {
1318 _view->foreach_regionview (
1319 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1322 _view->foreach_selected_regionview (
1323 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1329 MidiTimeAxisView::add_note_selection (uint8_t note)
1331 if (!_editor.internal_editing()) {
1335 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1337 if (_view->num_selected_regionviews() == 0) {
1338 _view->foreach_regionview (
1339 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1342 _view->foreach_selected_regionview (
1343 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1349 MidiTimeAxisView::extend_note_selection (uint8_t note)
1351 if (!_editor.internal_editing()) {
1355 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1357 if (_view->num_selected_regionviews() == 0) {
1358 _view->foreach_regionview (
1359 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1362 _view->foreach_selected_regionview (
1363 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1369 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1371 if (!_editor.internal_editing()) {
1375 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1377 if (_view->num_selected_regionviews() == 0) {
1378 _view->foreach_regionview (
1379 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1382 _view->foreach_selected_regionview (
1383 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1389 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1391 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1395 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1397 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1401 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1403 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1407 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1409 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1413 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1415 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1419 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1420 bool changed = false;
1424 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1426 for (uint32_t chn = 0; chn < 16; ++chn) {
1427 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1428 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1434 if ((selected_channels & (0x0001 << chn)) == 0) {
1435 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1436 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1438 changed = track->set_marked_for_display (false) || changed;
1440 changed = track->set_marked_for_display (true) || changed;
1447 /* TODO: Bender, Pressure */
1449 /* invalidate the controller menu, so that we rebuild it next time */
1450 _controller_menu_map.clear ();
1451 delete controller_menu;
1452 controller_menu = 0;
1460 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1462 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1467 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1468 if (i != _controller_menu_map.end()) {
1472 i = _channel_command_menu_map.find (param);
1473 if (i != _channel_command_menu_map.end()) {
1480 boost::shared_ptr<MidiRegion>
1481 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1483 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1485 real_editor->begin_reversible_command (Operations::create_region);
1486 playlist()->clear_changes ();
1488 real_editor->snap_to (pos, RoundNearest);
1490 boost::shared_ptr<Source> src = _session->create_midi_source_by_stealing_name (view()->trackview().track());
1493 plist.add (ARDOUR::Properties::start, 0);
1494 plist.add (ARDOUR::Properties::length, length);
1495 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1497 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1499 playlist()->add_region (region, pos);
1500 _session->add_command (new StatefulDiffCommand (playlist()));
1503 real_editor->commit_reversible_command ();
1506 return boost::dynamic_pointer_cast<MidiRegion>(region);
1510 MidiTimeAxisView::ensure_step_editor ()
1512 if (!_step_editor) {
1513 _step_editor = new StepEditor (_editor, midi_track(), *this);
1518 MidiTimeAxisView::start_step_editing ()
1520 ensure_step_editor ();
1521 _step_editor->start_step_editing ();
1525 MidiTimeAxisView::stop_step_editing ()
1528 _step_editor->stop_step_editing ();
1532 /** @return channel (counted from 0) to add an event to, based on the current setting
1533 * of the channel selector.
1536 MidiTimeAxisView::get_channel_for_add () const
1538 uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
1540 uint8_t channel = 0;
1542 /* pick the highest selected channel, unless all channels are selected,
1543 which is interpreted to mean channel 1 (zero)
1546 for (uint16_t i = 0; i < 16; ++i) {
1547 if (chn_mask & (1<<i)) {
1553 if (chn_cnt == 16) {
1561 MidiTimeAxisView::note_range_changed ()
1563 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1564 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1568 MidiTimeAxisView::contents_height_changed ()
1570 _range_scroomer->queue_resize ();
1574 MidiTimeAxisView::playback_channel_mode_changed ()
1576 switch (midi_track()->get_playback_channel_mode()) {
1578 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("all")));
1580 case FilterChannels:
1581 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("some")));
1584 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Play"), _("all"), PBD::ffs (midi_track()->get_playback_channel_mask())));
1590 MidiTimeAxisView::capture_channel_mode_changed ()
1592 switch (midi_track()->get_capture_channel_mode()) {
1594 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("all")));
1596 case FilterChannels:
1597 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("some")));
1600 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Rec"), _("all"), PBD::ffs (midi_track()->get_capture_channel_mask())));