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/playlist.h"
50 #include "ardour/region.h"
51 #include "ardour/region_factory.h"
52 #include "ardour/route.h"
53 #include "ardour/session.h"
54 #include "ardour/session_object.h"
55 #include "ardour/source.h"
56 #include "ardour/track.h"
57 #include "ardour/types.h"
59 #include "ardour_ui.h"
60 #include "ardour_button.h"
61 #include "automation_line.h"
62 #include "automation_time_axis.h"
65 #include "ghostregion.h"
66 #include "gui_thread.h"
68 #include "midi_channel_selector.h"
69 #include "midi_scroomer.h"
70 #include "midi_streamview.h"
71 #include "midi_region_view.h"
72 #include "midi_time_axis.h"
73 #include "piano_roll_header.h"
74 #include "playlist_selector.h"
75 #include "plugin_selector.h"
76 #include "plugin_ui.h"
77 #include "point_selection.h"
79 #include "region_view.h"
80 #include "rgb_macros.h"
81 #include "selection.h"
82 #include "step_editor.h"
84 #include "note_base.h"
86 #include "ardour/midi_track.h"
90 using namespace ARDOUR;
93 using namespace Gtkmm2ext;
94 using namespace Editing;
96 // Minimum height at which a control is displayed
97 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 140;
98 static const uint32_t KEYBOARD_MIN_HEIGHT = 130;
100 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
101 : AxisView(sess) // virtually inherited
102 , RouteTimeAxisView(ed, sess, canvas)
103 , _ignore_signals(false)
105 , _piano_roll_header(0)
106 , _note_mode(Sustained)
108 , _percussion_mode_item(0)
109 , _color_mode(MeterColors)
110 , _meter_color_mode_item(0)
111 , _channel_color_mode_item(0)
112 , _track_color_mode_item(0)
113 , _channel_selector (0)
114 , _step_edit_item (0)
115 , controller_menu (0)
121 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
125 _view = new MidiStreamView (*this);
128 _piano_roll_header = new PianoRollHeader(*midi_view());
129 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
130 _range_scroomer->DoubleClicked.connect (
131 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
132 MidiStreamView::ContentsRange, false));
135 /* This next call will result in our height being set up, so it must come after
136 the creation of the piano roll / range scroomer as their visibility is set up
139 RouteTimeAxisView::set_route (rt);
141 _view->apply_color (_color, StreamView::RegionColor);
143 subplugin_menu.set_name ("ArdourContextMenu");
145 if (!gui_property ("note-range-min").empty ()) {
146 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
147 atoi (gui_property ("note-range-max").c_str()),
151 midi_view()->NoteRangeChanged.connect (
152 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
153 _view->ContentsHeightChanged.connect (
154 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
156 ignore_toggle = false;
158 if (is_midi_track()) {
159 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
160 _note_mode = midi_track()->note_mode();
161 } else { // MIDI bus (which doesn't exist yet..)
162 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
165 /* map current state of the route */
167 processors_changed (RouteProcessorChange ());
169 _route->processors_changed.connect (*this, invalidator (*this),
170 boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
174 _piano_roll_header->SetNoteSelection.connect (
175 sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
176 _piano_roll_header->AddNoteSelection.connect (
177 sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
178 _piano_roll_header->ExtendNoteSelection.connect (
179 sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
180 _piano_roll_header->ToggleNoteSelection.connect (
181 sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
183 /* Suspend updates of the StreamView during scroomer drags to speed things up */
184 _range_scroomer->DragStarting.connect (
185 sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
186 _range_scroomer->DragFinishing.connect (
187 sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
189 /* Put the scroomer and the keyboard in a VBox with a padding
190 label so that they can be reduced in height for stacked-view
193 VBox* v = manage (new VBox);
194 HBox* h = manage (new HBox);
195 h->pack_start (*_range_scroomer);
196 h->pack_start (*_piano_roll_header);
197 v->pack_start (*h, false, false);
198 v->pack_start (*manage (new Label ("")), true, true);
201 controls_hbox.pack_start(*v, false, false);
203 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
204 controls_base_selected_name = "MidiTrackControlsBaseSelected";
205 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
207 midi_view()->NoteRangeChanged.connect (
208 sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
210 /* ask for notifications of any new RegionViews */
211 _view->RegionViewAdded.connect (
212 sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
214 midi_track()->PlaybackChannelModeChanged.connect (*this, invalidator (*this),
215 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
217 midi_track()->PlaybackChannelMaskChanged.connect (*this, invalidator (*this),
218 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
220 midi_track()->CaptureChannelModeChanged.connect (*this, invalidator (*this),
221 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
223 midi_track()->CaptureChannelMaskChanged.connect (*this, invalidator (*this),
224 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
227 playback_channel_mode_changed ();
228 capture_channel_mode_changed ();
230 if (!_editor.have_idled()) {
231 /* first idle will do what we need */
237 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
239 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
240 for (; m != patch_manager.all_models().end(); ++m) {
241 _midnam_model_selector.append_text(m->c_str());
244 if (gui_property (X_("midnam-model-name")).empty()) {
245 set_gui_property (X_("midnam-model-name"), "Generic");
248 if (gui_property (X_("midnam-custom-device-mode")).empty()) {
249 boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
251 set_gui_property (X_("midnam-custom-device-mode"),
252 *device_names->custom_device_mode_names().begin());
256 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
257 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
259 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
260 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
262 _midi_controls_box.set_homogeneous(false);
263 _midi_controls_box.set_border_width (10);
265 _channel_status_box.set_homogeneous (false);
266 _channel_status_box.set_spacing (6);
268 _channel_selector_button.set_label (_("Chns"));
269 ARDOUR_UI::instance()->set_tip (_channel_selector_button, _("Click to edit channel settings"));
271 /* fixed sized labels to prevent silly nonsense (though obviously,
272 * they cause their own too)
275 _playback_channel_status.set_size_request (65, -1);
276 _capture_channel_status.set_size_request (60, -1);
278 _channel_status_box.pack_start (_playback_channel_status, false, false);
279 _channel_status_box.pack_start (_capture_channel_status, false, false);
280 _channel_status_box.pack_start (_channel_selector_button, false, false);
281 _channel_status_box.show_all ();
283 _channel_selector_button.signal_clicked().connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_channel_selector));
285 _midi_controls_box.pack_start (_channel_status_box, false, false, 10);
287 if (!patch_manager.all_models().empty()) {
289 _midnam_model_selector.set_size_request(22, 30);
290 _midnam_model_selector.set_border_width(2);
291 _midnam_model_selector.show ();
292 _midi_controls_box.pack_start (_midnam_model_selector);
294 _midnam_custom_device_mode_selector.set_size_request(10, 30);
295 _midnam_custom_device_mode_selector.set_border_width(2);
296 _midnam_custom_device_mode_selector.show ();
298 _midi_controls_box.pack_start (_midnam_custom_device_mode_selector);
302 custom_device_mode_changed();
304 _midnam_model_selector.signal_changed().connect(
305 sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
306 _midnam_custom_device_mode_selector.signal_changed().connect(
307 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
309 controls_vbox.pack_start(_midi_controls_box, false, false);
311 const string color_mode = gui_property ("color-mode");
312 if (!color_mode.empty()) {
313 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
314 if (_channel_selector && _color_mode == ChannelColors) {
315 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
319 set_color_mode (_color_mode, true, false);
321 const string note_mode = gui_property ("note-mode");
322 if (!note_mode.empty()) {
323 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
324 if (_percussion_mode_item) {
325 _percussion_mode_item->set_active (_note_mode == Percussive);
329 /* Look for any GUI object state nodes that represent automation children
330 * that should exist, and create the children.
333 const list<string> gui_ids = gui_object_state().all_ids ();
334 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
337 Evoral::Parameter parameter (0, 0, 0);
339 bool const p = AutomationTimeAxisView::parse_state_id (
340 *i, route_id, has_parameter, parameter);
341 if (p && route_id == _route->id () && has_parameter) {
342 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
343 create_automation_child (parameter, string_is_affirmative (visible));
349 MidiTimeAxisView::first_idle ()
356 MidiTimeAxisView::~MidiTimeAxisView ()
358 delete _channel_selector;
360 delete _piano_roll_header;
361 _piano_roll_header = 0;
363 delete _range_scroomer;
366 delete controller_menu;
371 MidiTimeAxisView::enter_internal_edit_mode ()
374 midi_view()->enter_internal_edit_mode ();
379 MidiTimeAxisView::leave_internal_edit_mode ()
382 midi_view()->leave_internal_edit_mode ();
387 MidiTimeAxisView::check_step_edit ()
389 ensure_step_editor ();
390 _step_editor->check_step_edit ();
394 MidiTimeAxisView::model_changed()
396 const Glib::ustring model = _midnam_model_selector.get_active_text();
397 set_gui_property (X_("midnam-model-name"), model);
399 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
400 .custom_device_mode_names_by_model(model);
402 _midnam_custom_device_mode_selector.clear_items();
404 for (std::list<std::string>::const_iterator i = device_modes.begin();
405 i != device_modes.end(); ++i) {
406 _midnam_custom_device_mode_selector.append_text(*i);
409 _midnam_custom_device_mode_selector.set_active(0);
411 _route->instrument_info().set_external_instrument (
412 _midnam_model_selector.get_active_text(),
413 _midnam_custom_device_mode_selector.get_active_text());
415 // Rebuild controller menu
416 _controller_menu_map.clear ();
417 delete controller_menu;
419 build_automation_action_menu(false);
423 MidiTimeAxisView::custom_device_mode_changed()
425 const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text();
426 set_gui_property (X_("midnam-custom-device-mode"), mode);
427 _route->instrument_info().set_external_instrument (
428 _midnam_model_selector.get_active_text(), mode);
432 MidiTimeAxisView::midi_view()
434 return dynamic_cast<MidiStreamView*>(_view);
438 MidiTimeAxisView::set_height (uint32_t h)
440 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
441 _midi_controls_box.show ();
443 _midi_controls_box.hide();
446 if (h >= KEYBOARD_MIN_HEIGHT) {
447 if (is_track() && _range_scroomer) {
448 _range_scroomer->show();
450 if (is_track() && _piano_roll_header) {
451 _piano_roll_header->show();
454 if (is_track() && _range_scroomer) {
455 _range_scroomer->hide();
457 if (is_track() && _piano_roll_header) {
458 _piano_roll_header->hide();
462 /* We need to do this after changing visibility of our stuff, as it will
463 eventually trigger a call to Editor::reset_controls_layout_width(),
464 which needs to know if we have just shown or hidden a scroomer /
467 RouteTimeAxisView::set_height (h);
471 MidiTimeAxisView::append_extra_display_menu_items ()
473 using namespace Menu_Helpers;
475 MenuList& items = display_menu->items();
478 Menu *range_menu = manage(new Menu);
479 MenuList& range_items = range_menu->items();
480 range_menu->set_name ("ArdourContextMenu");
482 range_items.push_back (
483 MenuElem (_("Show Full Range"),
484 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
485 MidiStreamView::FullRange, true)));
487 range_items.push_back (
488 MenuElem (_("Fit Contents"),
489 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
490 MidiStreamView::ContentsRange, true)));
492 items.push_back (MenuElem (_("Note Range"), *range_menu));
493 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
494 items.push_back (MenuElem (_("Channel Selector"),
495 sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector)));
497 color_mode_menu = build_color_mode_menu();
498 if (color_mode_menu) {
499 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
502 items.push_back (SeparatorElem ());
506 MidiTimeAxisView::toggle_channel_selector ()
508 if (!_channel_selector) {
509 _channel_selector = new MidiChannelSelectorWindow (midi_track());
511 if (_color_mode == ChannelColors) {
512 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
514 _channel_selector->set_default_channel_color ();
517 _channel_selector->show_all ();
519 _channel_selector->cycle_visibility ();
524 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
526 using namespace Menu_Helpers;
528 /* If we have a controller menu, we need to detach it before
529 RouteTimeAxis::build_automation_action_menu destroys the
530 menu it is attached to. Otherwise GTK destroys
531 controller_menu's gobj, meaning that it can't be reattached
532 below. See bug #3134.
535 if (controller_menu) {
536 detach_menu (*controller_menu);
539 _channel_command_menu_map.clear ();
540 RouteTimeAxisView::build_automation_action_menu (for_selection);
542 MenuList& automation_items = automation_action_menu->items();
544 uint16_t selected_channels = midi_track()->get_playback_channel_mask();
546 if (selected_channels != 0) {
548 automation_items.push_back (SeparatorElem());
550 /* these 2 MIDI "command" types are semantically more like automation
551 than note data, but they are not MIDI controllers. We give them
552 special status in this menu, since they will not show up in the
553 controller list and anyone who actually knows something about MIDI
554 (!) would not expect to find them there.
557 add_channel_command_menu_item (
558 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
559 automation_items.back().set_sensitive (
560 !for_selection || _editor.get_selection().tracks.size() == 1);
561 add_channel_command_menu_item (
562 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
563 automation_items.back().set_sensitive (
564 !for_selection || _editor.get_selection().tracks.size() == 1);
566 /* now all MIDI controllers. Always offer the possibility that we will
567 rebuild the controllers menu since it might need to be updated after
568 a channel mode change or other change. Also detach it first in case
569 it has been used anywhere else.
572 build_controller_menu ();
574 automation_items.push_back (SeparatorElem());
575 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
576 automation_items.back().set_sensitive (
577 !for_selection || _editor.get_selection().tracks.size() == 1);
579 automation_items.push_back (
580 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
581 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
586 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
588 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
590 for (uint8_t chn = 0; chn < 16; chn++) {
591 if (selected_channels & (0x0001 << chn)) {
593 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
594 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
597 menu->set_active (yn);
604 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
606 AutomationType auto_type,
609 using namespace Menu_Helpers;
611 /* count the number of selected channels because we will build a different menu
612 structure if there is more than 1 selected.
615 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
618 for (uint8_t chn = 0; chn < 16; chn++) {
619 if (selected_channels & (0x0001 << chn)) {
628 /* multiple channels - create a submenu, with 1 item per channel */
630 Menu* chn_menu = manage (new Menu);
631 MenuList& chn_items (chn_menu->items());
632 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
634 /* add a couple of items to hide/show all of them */
636 chn_items.push_back (
637 MenuElem (_("Hide all channels"),
638 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
639 false, param_without_channel)));
640 chn_items.push_back (
641 MenuElem (_("Show all channels"),
642 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
643 true, param_without_channel)));
645 for (uint8_t chn = 0; chn < 16; chn++) {
646 if (selected_channels & (0x0001 << chn)) {
648 /* for each selected channel, add a menu item for this controller */
650 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
651 chn_items.push_back (
652 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
653 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
654 fully_qualified_param)));
656 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
657 bool visible = false;
660 if (track->marked_for_display()) {
665 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
666 _channel_command_menu_map[fully_qualified_param] = cmi;
667 cmi->set_active (visible);
671 /* now create an item in the parent menu that has the per-channel list as a submenu */
673 items.push_back (MenuElem (label, *chn_menu));
677 /* just one channel - create a single menu item for this command+channel combination*/
679 for (uint8_t chn = 0; chn < 16; chn++) {
680 if (selected_channels & (0x0001 << chn)) {
682 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
684 CheckMenuElem (label,
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*>(&items.back());
698 _channel_command_menu_map[fully_qualified_param] = cmi;
699 cmi->set_active (visible);
701 /* one channel only */
708 /** Add a single menu item for a controller on one channel. */
710 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
712 const std::string& name)
714 using namespace Menu_Helpers;
716 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
717 for (uint8_t chn = 0; chn < 16; chn++) {
718 if (selected_channels & (0x0001 << chn)) {
720 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
721 ctl_items.push_back (
723 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
725 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
726 fully_qualified_param)));
727 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
729 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
730 fully_qualified_param);
732 bool visible = false;
734 if (track->marked_for_display()) {
739 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&ctl_items.back());
740 _controller_menu_map[fully_qualified_param] = cmi;
741 cmi->set_active (visible);
743 /* one channel only */
749 /** Add a submenu with 1 item per channel for a controller on many channels. */
751 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
753 const std::string& name)
755 using namespace Menu_Helpers;
757 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
759 Menu* chn_menu = manage (new Menu);
760 MenuList& chn_items (chn_menu->items());
762 /* add a couple of items to hide/show this controller on all channels */
764 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
765 chn_items.push_back (
766 MenuElem (_("Hide all channels"),
767 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
768 false, param_without_channel)));
769 chn_items.push_back (
770 MenuElem (_("Show all channels"),
771 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
772 true, param_without_channel)));
774 for (uint8_t chn = 0; chn < 16; chn++) {
775 if (selected_channels & (0x0001 << chn)) {
777 /* for each selected channel, add a menu item for this controller */
779 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
780 chn_items.push_back (
781 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
782 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
783 fully_qualified_param)));
785 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
786 fully_qualified_param);
787 bool visible = false;
790 if (track->marked_for_display()) {
795 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
796 _controller_menu_map[fully_qualified_param] = cmi;
797 cmi->set_active (visible);
801 /* add the per-channel menu to the list of controllers, with the name of the controller */
802 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
804 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
807 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
808 MidiTimeAxisView::get_device_mode()
810 using namespace MIDI::Name;
812 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
814 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
817 return device_names->custom_device_mode_by_name(
818 gui_property (X_("midnam-custom-device-mode")));
821 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
822 MidiTimeAxisView::get_device_names()
824 using namespace MIDI::Name;
826 const std::string model = gui_property (X_("midnam-model-name"));
828 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
829 .document_by_model(model);
831 return midnam->master_device_names(model);
833 return boost::shared_ptr<MasterDeviceNames>();
838 MidiTimeAxisView::build_controller_menu ()
840 using namespace Menu_Helpers;
842 if (controller_menu) {
843 /* it exists and has not been invalidated by a channel mode change */
847 controller_menu = new Menu; // explicitly managed by us
848 MenuList& items (controller_menu->items());
850 /* create several "top level" menu items for sets of controllers (16 at a
851 time), and populate each one with a submenu for each controller+channel
852 combination covering the currently selected channels for this track
855 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
857 /* count the number of selected channels because we will build a different menu
858 structure if there is more than 1 selected.
862 for (uint8_t chn = 0; chn < 16; chn++) {
863 if (selected_channels & (0x0001 << chn)) {
870 using namespace MIDI::Name;
871 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
873 if (device_names && !device_names->controls().empty()) {
874 /* Controllers names available in midnam file, generate fancy menu */
875 unsigned n_items = 0;
876 unsigned n_groups = 0;
878 /* TODO: This is not correct, should look up the currently applicable ControlNameList
879 and only build a menu for that one. */
880 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
881 l != device_names->controls().end(); ++l) {
882 boost::shared_ptr<ControlNameList> name_list = l->second;
883 Menu* ctl_menu = NULL;
885 for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
886 c != name_list->controls().end();) {
887 const uint16_t ctl = c->second->number();
888 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
889 /* Skip bank select controllers since they're handled specially */
891 /* Create a new submenu */
892 ctl_menu = manage (new Menu);
895 MenuList& ctl_items (ctl_menu->items());
897 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
899 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
904 if (ctl_menu && (++n_items == 16 || c == name_list->controls().end())) {
905 /* Submenu has 16 items or we're done, add it to controller menu and reset */
907 MenuElem(string_compose(_("Controllers %1-%2"),
908 (16 * n_groups), (16 * n_groups) + n_items - 1),
917 /* No controllers names, generate generic numeric menu */
918 for (int i = 0; i < 127; i += 16) {
919 Menu* ctl_menu = manage (new Menu);
920 MenuList& ctl_items (ctl_menu->items());
922 for (int ctl = i; ctl < i+16; ++ctl) {
923 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
924 /* Skip bank select controllers since they're handled specially */
929 add_multi_channel_controller_item(
930 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
932 add_single_channel_controller_item(
933 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
937 /* Add submenu for this block of controllers to controller menu */
939 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
946 MidiTimeAxisView::build_note_mode_menu()
948 using namespace Menu_Helpers;
950 Menu* mode_menu = manage (new Menu);
951 MenuList& items = mode_menu->items();
952 mode_menu->set_name ("ArdourContextMenu");
954 RadioMenuItem::Group mode_group;
956 RadioMenuElem (mode_group,_("Sustained"),
957 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
959 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
960 _note_mode_item->set_active(_note_mode == Sustained);
963 RadioMenuElem (mode_group, _("Percussive"),
964 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
966 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
967 _percussion_mode_item->set_active(_note_mode == Percussive);
973 MidiTimeAxisView::build_color_mode_menu()
975 using namespace Menu_Helpers;
977 Menu* mode_menu = manage (new Menu);
978 MenuList& items = mode_menu->items();
979 mode_menu->set_name ("ArdourContextMenu");
981 RadioMenuItem::Group mode_group;
983 RadioMenuElem (mode_group, _("Meter Colors"),
984 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
985 MeterColors, false, true, true)));
986 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
987 _meter_color_mode_item->set_active(_color_mode == MeterColors);
990 RadioMenuElem (mode_group, _("Channel Colors"),
991 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
992 ChannelColors, false, true, true)));
993 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
994 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
997 RadioMenuElem (mode_group, _("Track Color"),
998 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
999 TrackColor, false, true, true)));
1000 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1001 _channel_color_mode_item->set_active(_color_mode == TrackColor);
1007 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
1009 if (apply_to_selection) {
1010 _editor.get_selection().tracks.foreach_midi_time_axis (
1011 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
1013 if (_note_mode != mode || midi_track()->note_mode() != mode) {
1015 midi_track()->set_note_mode(mode);
1016 set_gui_property ("note-mode", enum_2_string(_note_mode));
1017 _view->redisplay_track();
1023 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
1025 if (apply_to_selection) {
1026 _editor.get_selection().tracks.foreach_midi_time_axis (
1027 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
1029 if (_color_mode == mode && !force) {
1033 if (_channel_selector) {
1034 if (mode == ChannelColors) {
1035 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
1037 _channel_selector->set_default_channel_color();
1042 set_gui_property ("color-mode", enum_2_string(_color_mode));
1044 _view->redisplay_track();
1050 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1052 if (apply_to_selection) {
1053 _editor.get_selection().tracks.foreach_midi_time_axis (
1054 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1056 if (!_ignore_signals) {
1057 midi_view()->set_note_range(range);
1063 MidiTimeAxisView::update_range()
1065 MidiGhostRegion* mgr;
1067 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1068 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1069 mgr->update_range();
1075 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1077 if (apply_to_selection) {
1078 _editor.get_selection().tracks.foreach_midi_time_axis (
1079 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1082 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1084 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1085 create_automation_child(*i, true);
1089 RouteTimeAxisView::show_all_automation ();
1094 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1096 if (apply_to_selection) {
1097 _editor.get_selection().tracks.foreach_midi_time_axis (
1098 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1101 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1103 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1104 create_automation_child (*i, true);
1108 RouteTimeAxisView::show_existing_automation ();
1112 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1115 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1117 if (param.type() == NullAutomation) {
1121 AutomationTracks::iterator existing = _automation_tracks.find (param);
1123 if (existing != _automation_tracks.end()) {
1125 /* automation track created because we had existing data for
1126 * the processor, but visibility may need to be controlled
1127 * since it will have been set visible by default.
1130 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1137 boost::shared_ptr<AutomationTimeAxisView> track;
1139 switch (param.type()) {
1141 case GainAutomation:
1142 create_gain_automation_child (param, show);
1145 case PluginAutomation:
1146 /* handled elsewhere */
1149 case MidiCCAutomation:
1150 case MidiPgmChangeAutomation:
1151 case MidiPitchBenderAutomation:
1152 case MidiChannelPressureAutomation:
1153 case MidiSystemExclusiveAutomation:
1154 /* These controllers are region "automation" - they are owned
1155 * by regions (and their MidiModels), not by the track. As a
1156 * result we do not create an AutomationList/Line for the track
1157 * ... except here we are doing something!! XXX
1160 track.reset (new AutomationTimeAxisView (
1163 boost::shared_ptr<Automatable> (),
1164 boost::shared_ptr<AutomationControl> (),
1170 _route->describe_parameter(param)));
1173 _view->foreach_regionview (
1174 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1177 add_automation_child (param, track, show);
1181 error << "MidiTimeAxisView: unknown automation child "
1182 << EventTypeMap::instance().to_symbol(param) << endmsg;
1187 MidiTimeAxisView::route_active_changed ()
1189 RouteUI::route_active_changed ();
1192 if (_route->active()) {
1193 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1194 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1195 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1197 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1198 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1199 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1202 if (_route->active()) {
1203 controls_ebox.set_name ("BusControlsBaseUnselected");
1204 controls_base_selected_name = "BusControlsBaseSelected";
1205 controls_base_unselected_name = "BusControlsBaseUnselected";
1207 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1208 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1209 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1215 MidiTimeAxisView::set_note_selection (uint8_t note)
1217 if (!_editor.internal_editing()) {
1221 uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1223 if (_view->num_selected_regionviews() == 0) {
1224 _view->foreach_regionview (
1225 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1228 _view->foreach_selected_regionview (
1229 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1235 MidiTimeAxisView::add_note_selection (uint8_t note)
1237 if (!_editor.internal_editing()) {
1241 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1243 if (_view->num_selected_regionviews() == 0) {
1244 _view->foreach_regionview (
1245 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1248 _view->foreach_selected_regionview (
1249 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1255 MidiTimeAxisView::extend_note_selection (uint8_t note)
1257 if (!_editor.internal_editing()) {
1261 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1263 if (_view->num_selected_regionviews() == 0) {
1264 _view->foreach_regionview (
1265 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1268 _view->foreach_selected_regionview (
1269 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1275 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1277 if (!_editor.internal_editing()) {
1281 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1283 if (_view->num_selected_regionviews() == 0) {
1284 _view->foreach_regionview (
1285 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1288 _view->foreach_selected_regionview (
1289 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1295 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1297 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1301 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1303 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1307 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1309 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1313 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1315 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1319 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1321 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1325 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1326 bool changed = false;
1330 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1332 for (uint32_t chn = 0; chn < 16; ++chn) {
1333 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1334 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1340 if ((selected_channels & (0x0001 << chn)) == 0) {
1341 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1342 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1344 changed = track->set_marked_for_display (false) || changed;
1346 changed = track->set_marked_for_display (true) || changed;
1353 /* TODO: Bender, Pressure */
1355 /* invalidate the controller menu, so that we rebuild it next time */
1356 _controller_menu_map.clear ();
1357 delete controller_menu;
1358 controller_menu = 0;
1366 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1368 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1373 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1374 if (i != _controller_menu_map.end()) {
1378 i = _channel_command_menu_map.find (param);
1379 if (i != _channel_command_menu_map.end()) {
1386 boost::shared_ptr<MidiRegion>
1387 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1389 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1391 real_editor->begin_reversible_command (Operations::create_region);
1392 playlist()->clear_changes ();
1394 real_editor->snap_to (pos, 0);
1396 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1397 view()->trackview().track().get(), view()->trackview().track()->name());
1400 plist.add (ARDOUR::Properties::start, 0);
1401 plist.add (ARDOUR::Properties::length, length);
1402 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1404 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1406 playlist()->add_region (region, pos);
1407 _session->add_command (new StatefulDiffCommand (playlist()));
1410 real_editor->commit_reversible_command ();
1413 return boost::dynamic_pointer_cast<MidiRegion>(region);
1417 MidiTimeAxisView::ensure_step_editor ()
1419 if (!_step_editor) {
1420 _step_editor = new StepEditor (_editor, midi_track(), *this);
1425 MidiTimeAxisView::start_step_editing ()
1427 ensure_step_editor ();
1428 _step_editor->start_step_editing ();
1432 MidiTimeAxisView::stop_step_editing ()
1435 _step_editor->stop_step_editing ();
1439 /** @return channel (counted from 0) to add an event to, based on the current setting
1440 * of the channel selector.
1443 MidiTimeAxisView::get_channel_for_add () const
1445 uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
1447 uint8_t channel = 0;
1449 /* pick the highest selected channel, unless all channels are selected,
1450 which is interpreted to mean channel 1 (zero)
1453 for (uint16_t i = 0; i < 16; ++i) {
1454 if (chn_mask & (1<<i)) {
1460 if (chn_cnt == 16) {
1468 MidiTimeAxisView::note_range_changed ()
1470 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1471 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1475 MidiTimeAxisView::contents_height_changed ()
1477 _range_scroomer->set_size_request (-1, _view->child_height ());
1481 MidiTimeAxisView::playback_channel_mode_changed ()
1483 switch (midi_track()->get_playback_channel_mode()) {
1485 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("all")));
1487 case FilterChannels:
1488 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("some")));
1491 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Play"), _("all"), PBD::ffs (midi_track()->get_playback_channel_mask())));
1497 MidiTimeAxisView::capture_channel_mode_changed ()
1499 switch (midi_track()->get_capture_channel_mode()) {
1501 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("all")));
1503 case FilterChannels:
1504 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("some")));
1507 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Rec"), _("all"), PBD::ffs (midi_track()->get_capture_channel_mask())));