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);
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->set_position (WIN_POS_MOUSE);
519 _channel_selector->show_all ();
521 _channel_selector->cycle_visibility ();
526 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
528 using namespace Menu_Helpers;
530 /* If we have a controller menu, we need to detach it before
531 RouteTimeAxis::build_automation_action_menu destroys the
532 menu it is attached to. Otherwise GTK destroys
533 controller_menu's gobj, meaning that it can't be reattached
534 below. See bug #3134.
537 if (controller_menu) {
538 detach_menu (*controller_menu);
541 _channel_command_menu_map.clear ();
542 RouteTimeAxisView::build_automation_action_menu (for_selection);
544 MenuList& automation_items = automation_action_menu->items();
546 uint16_t selected_channels = midi_track()->get_playback_channel_mask();
548 if (selected_channels != 0) {
550 automation_items.push_back (SeparatorElem());
552 /* these 2 MIDI "command" types are semantically more like automation
553 than note data, but they are not MIDI controllers. We give them
554 special status in this menu, since they will not show up in the
555 controller list and anyone who actually knows something about MIDI
556 (!) would not expect to find them there.
559 add_channel_command_menu_item (
560 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
561 automation_items.back().set_sensitive (
562 !for_selection || _editor.get_selection().tracks.size() == 1);
563 add_channel_command_menu_item (
564 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
565 automation_items.back().set_sensitive (
566 !for_selection || _editor.get_selection().tracks.size() == 1);
568 /* now all MIDI controllers. Always offer the possibility that we will
569 rebuild the controllers menu since it might need to be updated after
570 a channel mode change or other change. Also detach it first in case
571 it has been used anywhere else.
574 build_controller_menu ();
576 automation_items.push_back (SeparatorElem());
577 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
578 automation_items.back().set_sensitive (
579 !for_selection || _editor.get_selection().tracks.size() == 1);
581 automation_items.push_back (
582 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
583 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
588 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
590 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
592 for (uint8_t chn = 0; chn < 16; chn++) {
593 if (selected_channels & (0x0001 << chn)) {
595 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
596 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
599 menu->set_active (yn);
606 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
608 AutomationType auto_type,
611 using namespace Menu_Helpers;
613 /* count the number of selected channels because we will build a different menu
614 structure if there is more than 1 selected.
617 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
620 for (uint8_t chn = 0; chn < 16; chn++) {
621 if (selected_channels & (0x0001 << chn)) {
630 /* multiple channels - create a submenu, with 1 item per channel */
632 Menu* chn_menu = manage (new Menu);
633 MenuList& chn_items (chn_menu->items());
634 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
636 /* add a couple of items to hide/show all of them */
638 chn_items.push_back (
639 MenuElem (_("Hide all channels"),
640 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
641 false, param_without_channel)));
642 chn_items.push_back (
643 MenuElem (_("Show all channels"),
644 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
645 true, param_without_channel)));
647 for (uint8_t chn = 0; chn < 16; chn++) {
648 if (selected_channels & (0x0001 << chn)) {
650 /* for each selected channel, add a menu item for this controller */
652 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
653 chn_items.push_back (
654 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
655 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
656 fully_qualified_param)));
658 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
659 bool visible = false;
662 if (track->marked_for_display()) {
667 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
668 _channel_command_menu_map[fully_qualified_param] = cmi;
669 cmi->set_active (visible);
673 /* now create an item in the parent menu that has the per-channel list as a submenu */
675 items.push_back (MenuElem (label, *chn_menu));
679 /* just one channel - create a single menu item for this command+channel combination*/
681 for (uint8_t chn = 0; chn < 16; chn++) {
682 if (selected_channels & (0x0001 << chn)) {
684 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
686 CheckMenuElem (label,
687 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
688 fully_qualified_param)));
690 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
691 bool visible = false;
694 if (track->marked_for_display()) {
699 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
700 _channel_command_menu_map[fully_qualified_param] = cmi;
701 cmi->set_active (visible);
703 /* one channel only */
710 /** Add a single menu item for a controller on one channel. */
712 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
714 const std::string& name)
716 using namespace Menu_Helpers;
718 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
719 for (uint8_t chn = 0; chn < 16; chn++) {
720 if (selected_channels & (0x0001 << chn)) {
722 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
723 ctl_items.push_back (
725 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
727 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
728 fully_qualified_param)));
729 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
731 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
732 fully_qualified_param);
734 bool visible = false;
736 if (track->marked_for_display()) {
741 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
742 _controller_menu_map[fully_qualified_param] = cmi;
743 cmi->set_active (visible);
745 /* one channel only */
751 /** Add a submenu with 1 item per channel for a controller on many channels. */
753 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
755 const std::string& name)
757 using namespace Menu_Helpers;
759 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
761 Menu* chn_menu = manage (new Menu);
762 MenuList& chn_items (chn_menu->items());
764 /* add a couple of items to hide/show this controller on all channels */
766 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
767 chn_items.push_back (
768 MenuElem (_("Hide all channels"),
769 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
770 false, param_without_channel)));
771 chn_items.push_back (
772 MenuElem (_("Show all channels"),
773 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
774 true, param_without_channel)));
776 for (uint8_t chn = 0; chn < 16; chn++) {
777 if (selected_channels & (0x0001 << chn)) {
779 /* for each selected channel, add a menu item for this controller */
781 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
782 chn_items.push_back (
783 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
784 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
785 fully_qualified_param)));
787 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
788 fully_qualified_param);
789 bool visible = false;
792 if (track->marked_for_display()) {
797 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
798 _controller_menu_map[fully_qualified_param] = cmi;
799 cmi->set_active (visible);
803 /* add the per-channel menu to the list of controllers, with the name of the controller */
804 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
806 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
809 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
810 MidiTimeAxisView::get_device_mode()
812 using namespace MIDI::Name;
814 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
816 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
819 return device_names->custom_device_mode_by_name(
820 gui_property (X_("midnam-custom-device-mode")));
823 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
824 MidiTimeAxisView::get_device_names()
826 using namespace MIDI::Name;
828 const std::string model = gui_property (X_("midnam-model-name"));
830 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
831 .document_by_model(model);
833 return midnam->master_device_names(model);
835 return boost::shared_ptr<MasterDeviceNames>();
840 MidiTimeAxisView::build_controller_menu ()
842 using namespace Menu_Helpers;
844 if (controller_menu) {
845 /* it exists and has not been invalidated by a channel mode change */
849 controller_menu = new Menu; // explicitly managed by us
850 MenuList& items (controller_menu->items());
852 /* create several "top level" menu items for sets of controllers (16 at a
853 time), and populate each one with a submenu for each controller+channel
854 combination covering the currently selected channels for this track
857 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
859 /* count the number of selected channels because we will build a different menu
860 structure if there is more than 1 selected.
864 for (uint8_t chn = 0; chn < 16; chn++) {
865 if (selected_channels & (0x0001 << chn)) {
872 using namespace MIDI::Name;
873 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
875 if (device_names && !device_names->controls().empty()) {
876 /* Controllers names available in midnam file, generate fancy menu */
877 unsigned n_items = 0;
878 unsigned n_groups = 0;
880 /* TODO: This is not correct, should look up the currently applicable ControlNameList
881 and only build a menu for that one. */
882 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
883 l != device_names->controls().end(); ++l) {
884 boost::shared_ptr<ControlNameList> name_list = l->second;
885 Menu* ctl_menu = NULL;
887 for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
888 c != name_list->controls().end();) {
889 const uint16_t ctl = c->second->number();
890 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
891 /* Skip bank select controllers since they're handled specially */
893 /* Create a new submenu */
894 ctl_menu = manage (new Menu);
897 MenuList& ctl_items (ctl_menu->items());
899 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
901 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
906 if (ctl_menu && (++n_items == 16 || c == name_list->controls().end())) {
907 /* Submenu has 16 items or we're done, add it to controller menu and reset */
909 MenuElem(string_compose(_("Controllers %1-%2"),
910 (16 * n_groups), (16 * n_groups) + n_items - 1),
919 /* No controllers names, generate generic numeric menu */
920 for (int i = 0; i < 127; i += 16) {
921 Menu* ctl_menu = manage (new Menu);
922 MenuList& ctl_items (ctl_menu->items());
924 for (int ctl = i; ctl < i+16; ++ctl) {
925 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
926 /* Skip bank select controllers since they're handled specially */
931 add_multi_channel_controller_item(
932 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
934 add_single_channel_controller_item(
935 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
939 /* Add submenu for this block of controllers to controller menu */
941 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
948 MidiTimeAxisView::build_note_mode_menu()
950 using namespace Menu_Helpers;
952 Menu* mode_menu = manage (new Menu);
953 MenuList& items = mode_menu->items();
954 mode_menu->set_name ("ArdourContextMenu");
956 RadioMenuItem::Group mode_group;
958 RadioMenuElem (mode_group,_("Sustained"),
959 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
961 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
962 _note_mode_item->set_active(_note_mode == Sustained);
965 RadioMenuElem (mode_group, _("Percussive"),
966 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
968 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
969 _percussion_mode_item->set_active(_note_mode == Percussive);
975 MidiTimeAxisView::build_color_mode_menu()
977 using namespace Menu_Helpers;
979 Menu* mode_menu = manage (new Menu);
980 MenuList& items = mode_menu->items();
981 mode_menu->set_name ("ArdourContextMenu");
983 RadioMenuItem::Group mode_group;
985 RadioMenuElem (mode_group, _("Meter Colors"),
986 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
987 MeterColors, false, true, true)));
988 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
989 _meter_color_mode_item->set_active(_color_mode == MeterColors);
992 RadioMenuElem (mode_group, _("Channel Colors"),
993 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
994 ChannelColors, false, true, true)));
995 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
996 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
999 RadioMenuElem (mode_group, _("Track Color"),
1000 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1001 TrackColor, false, true, true)));
1002 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1003 _channel_color_mode_item->set_active(_color_mode == TrackColor);
1009 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
1011 if (apply_to_selection) {
1012 _editor.get_selection().tracks.foreach_midi_time_axis (
1013 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
1015 if (_note_mode != mode || midi_track()->note_mode() != mode) {
1017 midi_track()->set_note_mode(mode);
1018 set_gui_property ("note-mode", enum_2_string(_note_mode));
1019 _view->redisplay_track();
1025 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
1027 if (apply_to_selection) {
1028 _editor.get_selection().tracks.foreach_midi_time_axis (
1029 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
1031 if (_color_mode == mode && !force) {
1035 if (_channel_selector) {
1036 if (mode == ChannelColors) {
1037 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
1039 _channel_selector->set_default_channel_color();
1044 set_gui_property ("color-mode", enum_2_string(_color_mode));
1046 _view->redisplay_track();
1052 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1054 if (apply_to_selection) {
1055 _editor.get_selection().tracks.foreach_midi_time_axis (
1056 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1058 if (!_ignore_signals) {
1059 midi_view()->set_note_range(range);
1065 MidiTimeAxisView::update_range()
1067 MidiGhostRegion* mgr;
1069 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1070 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1071 mgr->update_range();
1077 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1079 if (apply_to_selection) {
1080 _editor.get_selection().tracks.foreach_midi_time_axis (
1081 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1084 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1086 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1087 create_automation_child(*i, true);
1091 RouteTimeAxisView::show_all_automation ();
1096 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1098 if (apply_to_selection) {
1099 _editor.get_selection().tracks.foreach_midi_time_axis (
1100 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1103 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1105 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1106 create_automation_child (*i, true);
1110 RouteTimeAxisView::show_existing_automation ();
1114 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1117 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1119 if (param.type() == NullAutomation) {
1123 AutomationTracks::iterator existing = _automation_tracks.find (param);
1125 if (existing != _automation_tracks.end()) {
1127 /* automation track created because we had existing data for
1128 * the processor, but visibility may need to be controlled
1129 * since it will have been set visible by default.
1132 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1139 boost::shared_ptr<AutomationTimeAxisView> track;
1141 switch (param.type()) {
1143 case GainAutomation:
1144 create_gain_automation_child (param, show);
1147 case PluginAutomation:
1148 /* handled elsewhere */
1151 case MidiCCAutomation:
1152 case MidiPgmChangeAutomation:
1153 case MidiPitchBenderAutomation:
1154 case MidiChannelPressureAutomation:
1155 case MidiSystemExclusiveAutomation:
1156 /* These controllers are region "automation" - they are owned
1157 * by regions (and their MidiModels), not by the track. As a
1158 * result we do not create an AutomationList/Line for the track
1159 * ... except here we are doing something!! XXX
1162 track.reset (new AutomationTimeAxisView (
1165 boost::shared_ptr<Automatable> (),
1166 boost::shared_ptr<AutomationControl> (),
1172 _route->describe_parameter(param)));
1175 _view->foreach_regionview (
1176 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1179 add_automation_child (param, track, show);
1183 error << "MidiTimeAxisView: unknown automation child "
1184 << EventTypeMap::instance().to_symbol(param) << endmsg;
1189 MidiTimeAxisView::route_active_changed ()
1191 RouteUI::route_active_changed ();
1194 if (_route->active()) {
1195 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1196 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1197 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1199 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1200 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1201 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1204 if (_route->active()) {
1205 controls_ebox.set_name ("BusControlsBaseUnselected");
1206 controls_base_selected_name = "BusControlsBaseSelected";
1207 controls_base_unselected_name = "BusControlsBaseUnselected";
1209 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1210 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1211 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1217 MidiTimeAxisView::set_note_selection (uint8_t note)
1219 if (!_editor.internal_editing()) {
1223 uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1225 if (_view->num_selected_regionviews() == 0) {
1226 _view->foreach_regionview (
1227 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1230 _view->foreach_selected_regionview (
1231 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1237 MidiTimeAxisView::add_note_selection (uint8_t note)
1239 if (!_editor.internal_editing()) {
1243 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1245 if (_view->num_selected_regionviews() == 0) {
1246 _view->foreach_regionview (
1247 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1250 _view->foreach_selected_regionview (
1251 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1257 MidiTimeAxisView::extend_note_selection (uint8_t note)
1259 if (!_editor.internal_editing()) {
1263 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1265 if (_view->num_selected_regionviews() == 0) {
1266 _view->foreach_regionview (
1267 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1270 _view->foreach_selected_regionview (
1271 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1277 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1279 if (!_editor.internal_editing()) {
1283 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1285 if (_view->num_selected_regionviews() == 0) {
1286 _view->foreach_regionview (
1287 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1290 _view->foreach_selected_regionview (
1291 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1297 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1299 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1303 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1305 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1309 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1311 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1315 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1317 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1321 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1323 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1327 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1328 bool changed = false;
1332 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1334 for (uint32_t chn = 0; chn < 16; ++chn) {
1335 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1336 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1342 if ((selected_channels & (0x0001 << chn)) == 0) {
1343 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1344 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1346 changed = track->set_marked_for_display (false) || changed;
1348 changed = track->set_marked_for_display (true) || changed;
1355 /* TODO: Bender, Pressure */
1357 /* invalidate the controller menu, so that we rebuild it next time */
1358 _controller_menu_map.clear ();
1359 delete controller_menu;
1360 controller_menu = 0;
1368 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1370 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1375 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1376 if (i != _controller_menu_map.end()) {
1380 i = _channel_command_menu_map.find (param);
1381 if (i != _channel_command_menu_map.end()) {
1388 boost::shared_ptr<MidiRegion>
1389 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1391 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1393 real_editor->begin_reversible_command (Operations::create_region);
1394 playlist()->clear_changes ();
1396 real_editor->snap_to (pos, 0);
1398 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1399 view()->trackview().track().get(), view()->trackview().track()->name());
1402 plist.add (ARDOUR::Properties::start, 0);
1403 plist.add (ARDOUR::Properties::length, length);
1404 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1406 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1408 playlist()->add_region (region, pos);
1409 _session->add_command (new StatefulDiffCommand (playlist()));
1412 real_editor->commit_reversible_command ();
1415 return boost::dynamic_pointer_cast<MidiRegion>(region);
1419 MidiTimeAxisView::ensure_step_editor ()
1421 if (!_step_editor) {
1422 _step_editor = new StepEditor (_editor, midi_track(), *this);
1427 MidiTimeAxisView::start_step_editing ()
1429 ensure_step_editor ();
1430 _step_editor->start_step_editing ();
1434 MidiTimeAxisView::stop_step_editing ()
1437 _step_editor->stop_step_editing ();
1441 /** @return channel (counted from 0) to add an event to, based on the current setting
1442 * of the channel selector.
1445 MidiTimeAxisView::get_channel_for_add () const
1447 uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
1449 uint8_t channel = 0;
1451 /* pick the highest selected channel, unless all channels are selected,
1452 which is interpreted to mean channel 1 (zero)
1455 for (uint16_t i = 0; i < 16; ++i) {
1456 if (chn_mask & (1<<i)) {
1462 if (chn_cnt == 16) {
1470 MidiTimeAxisView::note_range_changed ()
1472 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1473 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1477 MidiTimeAxisView::contents_height_changed ()
1479 _range_scroomer->set_size_request (-1, _view->child_height ());
1483 MidiTimeAxisView::playback_channel_mode_changed ()
1485 switch (midi_track()->get_playback_channel_mode()) {
1487 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), ("all")));
1489 case FilterChannels:
1490 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), ("some")));
1493 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Play"), ("all"), ffs (midi_track()->get_playback_channel_mask())));
1499 MidiTimeAxisView::capture_channel_mode_changed ()
1501 switch (midi_track()->get_capture_channel_mode()) {
1503 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), ("all")));
1505 case FilterChannels:
1506 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), ("some")));
1509 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Rec"), ("all"), ffs (midi_track()->get_capture_channel_mask())));