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"
64 #include "canvas-note-event.h"
65 #include "canvas_impl.h"
68 #include "ghostregion.h"
69 #include "gui_thread.h"
71 #include "midi_channel_selector.h"
72 #include "midi_scroomer.h"
73 #include "midi_streamview.h"
74 #include "midi_region_view.h"
75 #include "midi_time_axis.h"
76 #include "piano_roll_header.h"
77 #include "playlist_selector.h"
78 #include "plugin_selector.h"
79 #include "plugin_ui.h"
80 #include "point_selection.h"
82 #include "region_view.h"
83 #include "rgb_macros.h"
84 #include "selection.h"
85 #include "step_editor.h"
86 #include "simplerect.h"
89 #include "ardour/midi_track.h"
93 using namespace ARDOUR;
96 using namespace Gtkmm2ext;
97 using namespace Editing;
99 // Minimum height at which a control is displayed
100 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 140;
101 static const uint32_t KEYBOARD_MIN_HEIGHT = 130;
103 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
104 : AxisView(sess) // virtually inherited
105 , RouteTimeAxisView(ed, sess, canvas)
106 , _ignore_signals(false)
108 , _piano_roll_header(0)
109 , _note_mode(Sustained)
111 , _percussion_mode_item(0)
112 , _color_mode(MeterColors)
113 , _meter_color_mode_item(0)
114 , _channel_color_mode_item(0)
115 , _track_color_mode_item(0)
116 , _channel_selector (0)
117 , _step_edit_item (0)
118 , controller_menu (0)
124 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
128 _view = new MidiStreamView (*this);
131 _piano_roll_header = new PianoRollHeader(*midi_view());
132 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
133 _range_scroomer->DoubleClicked.connect (
134 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
135 MidiStreamView::ContentsRange, false));
138 /* This next call will result in our height being set up, so it must come after
139 the creation of the piano roll / range scroomer as their visibility is set up
142 RouteTimeAxisView::set_route (rt);
144 _view->apply_color (_color, StreamView::RegionColor);
146 subplugin_menu.set_name ("ArdourContextMenu");
148 if (!gui_property ("note-range-min").empty ()) {
149 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
150 atoi (gui_property ("note-range-max").c_str()),
154 midi_view()->NoteRangeChanged.connect (
155 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
156 _view->ContentsHeightChanged.connect (
157 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
159 ignore_toggle = false;
161 if (is_midi_track()) {
162 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
163 _note_mode = midi_track()->note_mode();
164 } else { // MIDI bus (which doesn't exist yet..)
165 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
168 /* map current state of the route */
170 processors_changed (RouteProcessorChange ());
172 _route->processors_changed.connect (*this, invalidator (*this),
173 boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
177 _piano_roll_header->SetNoteSelection.connect (
178 sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
179 _piano_roll_header->AddNoteSelection.connect (
180 sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
181 _piano_roll_header->ExtendNoteSelection.connect (
182 sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
183 _piano_roll_header->ToggleNoteSelection.connect (
184 sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
186 /* Suspend updates of the StreamView during scroomer drags to speed things up */
187 _range_scroomer->DragStarting.connect (
188 sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
189 _range_scroomer->DragFinishing.connect (
190 sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
192 /* Put the scroomer and the keyboard in a VBox with a padding
193 label so that they can be reduced in height for stacked-view
196 VBox* v = manage (new VBox);
197 HBox* h = manage (new HBox);
198 h->pack_start (*_range_scroomer);
199 h->pack_start (*_piano_roll_header);
200 v->pack_start (*h, false, false);
201 v->pack_start (*manage (new Label ("")), true, true);
204 controls_hbox.pack_start(*v, false, false);
206 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
207 controls_base_selected_name = "MidiTrackControlsBaseSelected";
208 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
210 midi_view()->NoteRangeChanged.connect (
211 sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
213 /* ask for notifications of any new RegionViews */
214 _view->RegionViewAdded.connect (
215 sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
217 midi_track()->PlaybackChannelModeChanged.connect (*this, invalidator (*this),
218 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
220 midi_track()->PlaybackChannelMaskChanged.connect (*this, invalidator (*this),
221 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
223 midi_track()->CaptureChannelModeChanged.connect (*this, invalidator (*this),
224 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
226 midi_track()->CaptureChannelMaskChanged.connect (*this, invalidator (*this),
227 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
230 playback_channel_mode_changed ();
231 capture_channel_mode_changed ();
233 if (!_editor.have_idled()) {
234 /* first idle will do what we need */
240 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
242 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
243 for (; m != patch_manager.all_models().end(); ++m) {
244 _midnam_model_selector.append_text(m->c_str());
247 if (gui_property (X_("midnam-model-name")).empty()) {
248 set_gui_property (X_("midnam-model-name"), "Generic");
251 if (gui_property (X_("midnam-custom-device-mode")).empty()) {
252 boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
254 set_gui_property (X_("midnam-custom-device-mode"),
255 *device_names->custom_device_mode_names().begin());
259 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
260 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
262 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
263 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
265 _midi_controls_box.set_homogeneous(false);
266 _midi_controls_box.set_border_width (10);
268 _channel_status_box.set_homogeneous (false);
269 _channel_status_box.set_spacing (6);
271 _channel_selector_button.set_label (_("Chns"));
272 ARDOUR_UI::instance()->set_tip (_channel_selector_button, _("Click to edit channel settings"));
274 /* fixed sized labels to prevent silly nonsense (though obviously,
275 * they cause their own too)
278 _playback_channel_status.set_size_request (65, -1);
279 _capture_channel_status.set_size_request (60, -1);
281 _channel_status_box.pack_start (_playback_channel_status, false, false);
282 _channel_status_box.pack_start (_capture_channel_status, false, false);
283 _channel_status_box.pack_start (_channel_selector_button, false, false);
284 _channel_status_box.show_all ();
286 _channel_selector_button.signal_clicked().connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_channel_selector));
288 _midi_controls_box.pack_start (_channel_status_box, false, false, 10);
290 if (!patch_manager.all_models().empty()) {
292 _midnam_model_selector.set_size_request(22, 30);
293 _midnam_model_selector.set_border_width(2);
294 _midnam_model_selector.show ();
295 _midi_controls_box.pack_start (_midnam_model_selector);
297 _midnam_custom_device_mode_selector.set_size_request(10, 30);
298 _midnam_custom_device_mode_selector.set_border_width(2);
299 _midnam_custom_device_mode_selector.show ();
301 _midi_controls_box.pack_start (_midnam_custom_device_mode_selector);
305 custom_device_mode_changed();
307 _midnam_model_selector.signal_changed().connect(
308 sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
309 _midnam_custom_device_mode_selector.signal_changed().connect(
310 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
312 controls_vbox.pack_start(_midi_controls_box, false, false);
314 const string color_mode = gui_property ("color-mode");
315 if (!color_mode.empty()) {
316 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
317 if (_channel_selector && _color_mode == ChannelColors) {
318 _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
322 set_color_mode (_color_mode, true, false);
324 const string note_mode = gui_property ("note-mode");
325 if (!note_mode.empty()) {
326 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
327 if (_percussion_mode_item) {
328 _percussion_mode_item->set_active (_note_mode == Percussive);
332 /* Look for any GUI object state nodes that represent automation children
333 * that should exist, and create the children.
336 const list<string> gui_ids = gui_object_state().all_ids ();
337 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
340 Evoral::Parameter parameter (0, 0, 0);
342 bool const p = AutomationTimeAxisView::parse_state_id (
343 *i, route_id, has_parameter, parameter);
344 if (p && route_id == _route->id () && has_parameter) {
345 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
346 create_automation_child (parameter, string_is_affirmative (visible));
352 MidiTimeAxisView::first_idle ()
359 MidiTimeAxisView::~MidiTimeAxisView ()
361 delete _channel_selector;
363 delete _piano_roll_header;
364 _piano_roll_header = 0;
366 delete _range_scroomer;
369 delete controller_menu;
374 MidiTimeAxisView::enter_internal_edit_mode ()
377 midi_view()->enter_internal_edit_mode ();
382 MidiTimeAxisView::leave_internal_edit_mode ()
385 midi_view()->leave_internal_edit_mode ();
390 MidiTimeAxisView::check_step_edit ()
392 ensure_step_editor ();
393 _step_editor->check_step_edit ();
397 MidiTimeAxisView::model_changed()
399 const Glib::ustring model = _midnam_model_selector.get_active_text();
400 set_gui_property (X_("midnam-model-name"), model);
402 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
403 .custom_device_mode_names_by_model(model);
405 _midnam_custom_device_mode_selector.clear_items();
407 for (std::list<std::string>::const_iterator i = device_modes.begin();
408 i != device_modes.end(); ++i) {
409 _midnam_custom_device_mode_selector.append_text(*i);
412 _midnam_custom_device_mode_selector.set_active(0);
414 _route->instrument_info().set_external_instrument (
415 _midnam_model_selector.get_active_text(),
416 _midnam_custom_device_mode_selector.get_active_text());
418 // Rebuild controller menu
419 _controller_menu_map.clear ();
420 delete controller_menu;
422 build_automation_action_menu(false);
426 MidiTimeAxisView::custom_device_mode_changed()
428 const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text();
429 set_gui_property (X_("midnam-custom-device-mode"), mode);
430 _route->instrument_info().set_external_instrument (
431 _midnam_model_selector.get_active_text(), mode);
435 MidiTimeAxisView::midi_view()
437 return dynamic_cast<MidiStreamView*>(_view);
441 MidiTimeAxisView::set_height (uint32_t h)
443 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
444 _midi_controls_box.show ();
446 _midi_controls_box.hide();
449 if (h >= KEYBOARD_MIN_HEIGHT) {
450 if (is_track() && _range_scroomer) {
451 _range_scroomer->show();
453 if (is_track() && _piano_roll_header) {
454 _piano_roll_header->show();
457 if (is_track() && _range_scroomer) {
458 _range_scroomer->hide();
460 if (is_track() && _piano_roll_header) {
461 _piano_roll_header->hide();
465 /* We need to do this after changing visibility of our stuff, as it will
466 eventually trigger a call to Editor::reset_controls_layout_width(),
467 which needs to know if we have just shown or hidden a scroomer /
470 RouteTimeAxisView::set_height (h);
474 MidiTimeAxisView::append_extra_display_menu_items ()
476 using namespace Menu_Helpers;
478 MenuList& items = display_menu->items();
481 Menu *range_menu = manage(new Menu);
482 MenuList& range_items = range_menu->items();
483 range_menu->set_name ("ArdourContextMenu");
485 range_items.push_back (
486 MenuElem (_("Show Full Range"),
487 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
488 MidiStreamView::FullRange, true)));
490 range_items.push_back (
491 MenuElem (_("Fit Contents"),
492 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
493 MidiStreamView::ContentsRange, true)));
495 items.push_back (MenuElem (_("Note Range"), *range_menu));
496 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
497 items.push_back (MenuElem (_("Channel Selector"),
498 sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector)));
500 color_mode_menu = build_color_mode_menu();
501 if (color_mode_menu) {
502 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
505 items.push_back (SeparatorElem ());
509 MidiTimeAxisView::toggle_channel_selector ()
511 if (!_channel_selector) {
512 _channel_selector = new MidiChannelSelectorWindow (midi_track());
514 if (_color_mode == ChannelColors) {
515 _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
517 _channel_selector->set_default_channel_color ();
520 _channel_selector->show_all ();
522 _channel_selector->cycle_visibility ();
527 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
529 using namespace Menu_Helpers;
531 /* If we have a controller menu, we need to detach it before
532 RouteTimeAxis::build_automation_action_menu destroys the
533 menu it is attached to. Otherwise GTK destroys
534 controller_menu's gobj, meaning that it can't be reattached
535 below. See bug #3134.
538 if (controller_menu) {
539 detach_menu (*controller_menu);
542 _channel_command_menu_map.clear ();
543 RouteTimeAxisView::build_automation_action_menu (for_selection);
545 MenuList& automation_items = automation_action_menu->items();
547 uint16_t selected_channels = midi_track()->get_playback_channel_mask();
549 if (selected_channels != 0) {
551 automation_items.push_back (SeparatorElem());
553 /* these 2 MIDI "command" types are semantically more like automation
554 than note data, but they are not MIDI controllers. We give them
555 special status in this menu, since they will not show up in the
556 controller list and anyone who actually knows something about MIDI
557 (!) would not expect to find them there.
560 add_channel_command_menu_item (
561 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
562 automation_items.back().set_sensitive (
563 !for_selection || _editor.get_selection().tracks.size() == 1);
564 add_channel_command_menu_item (
565 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
566 automation_items.back().set_sensitive (
567 !for_selection || _editor.get_selection().tracks.size() == 1);
569 /* now all MIDI controllers. Always offer the possibility that we will
570 rebuild the controllers menu since it might need to be updated after
571 a channel mode change or other change. Also detach it first in case
572 it has been used anywhere else.
575 build_controller_menu ();
577 automation_items.push_back (SeparatorElem());
578 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
579 automation_items.back().set_sensitive (
580 !for_selection || _editor.get_selection().tracks.size() == 1);
582 automation_items.push_back (
583 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
584 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
589 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
591 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
593 for (uint8_t chn = 0; chn < 16; chn++) {
594 if (selected_channels & (0x0001 << chn)) {
596 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
597 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
600 menu->set_active (yn);
607 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
609 AutomationType auto_type,
612 using namespace Menu_Helpers;
614 /* count the number of selected channels because we will build a different menu
615 structure if there is more than 1 selected.
618 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
621 for (uint8_t chn = 0; chn < 16; chn++) {
622 if (selected_channels & (0x0001 << chn)) {
631 /* multiple channels - create a submenu, with 1 item per channel */
633 Menu* chn_menu = manage (new Menu);
634 MenuList& chn_items (chn_menu->items());
635 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
637 /* add a couple of items to hide/show all of them */
639 chn_items.push_back (
640 MenuElem (_("Hide all channels"),
641 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
642 false, param_without_channel)));
643 chn_items.push_back (
644 MenuElem (_("Show all channels"),
645 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
646 true, param_without_channel)));
648 for (uint8_t chn = 0; chn < 16; chn++) {
649 if (selected_channels & (0x0001 << chn)) {
651 /* for each selected channel, add a menu item for this controller */
653 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
654 chn_items.push_back (
655 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
656 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
657 fully_qualified_param)));
659 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
660 bool visible = false;
663 if (track->marked_for_display()) {
668 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
669 _channel_command_menu_map[fully_qualified_param] = cmi;
670 cmi->set_active (visible);
674 /* now create an item in the parent menu that has the per-channel list as a submenu */
676 items.push_back (MenuElem (label, *chn_menu));
680 /* just one channel - create a single menu item for this command+channel combination*/
682 for (uint8_t chn = 0; chn < 16; chn++) {
683 if (selected_channels & (0x0001 << chn)) {
685 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
687 CheckMenuElem (label,
688 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
689 fully_qualified_param)));
691 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
692 bool visible = false;
695 if (track->marked_for_display()) {
700 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
701 _channel_command_menu_map[fully_qualified_param] = cmi;
702 cmi->set_active (visible);
704 /* one channel only */
711 /** Add a single menu item for a controller on one channel. */
713 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
715 const std::string& name)
717 using namespace Menu_Helpers;
719 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
720 for (uint8_t chn = 0; chn < 16; chn++) {
721 if (selected_channels & (0x0001 << chn)) {
723 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
724 ctl_items.push_back (
726 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
728 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
729 fully_qualified_param)));
730 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
732 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
733 fully_qualified_param);
735 bool visible = false;
737 if (track->marked_for_display()) {
742 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
743 _controller_menu_map[fully_qualified_param] = cmi;
744 cmi->set_active (visible);
746 /* one channel only */
752 /** Add a submenu with 1 item per channel for a controller on many channels. */
754 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
756 const std::string& name)
758 using namespace Menu_Helpers;
760 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
762 Menu* chn_menu = manage (new Menu);
763 MenuList& chn_items (chn_menu->items());
765 /* add a couple of items to hide/show this controller on all channels */
767 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
768 chn_items.push_back (
769 MenuElem (_("Hide all channels"),
770 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
771 false, param_without_channel)));
772 chn_items.push_back (
773 MenuElem (_("Show all channels"),
774 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
775 true, param_without_channel)));
777 for (uint8_t chn = 0; chn < 16; chn++) {
778 if (selected_channels & (0x0001 << chn)) {
780 /* for each selected channel, add a menu item for this controller */
782 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
783 chn_items.push_back (
784 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
785 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
786 fully_qualified_param)));
788 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
789 fully_qualified_param);
790 bool visible = false;
793 if (track->marked_for_display()) {
798 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
799 _controller_menu_map[fully_qualified_param] = cmi;
800 cmi->set_active (visible);
804 /* add the per-channel menu to the list of controllers, with the name of the controller */
805 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
807 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
810 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
811 MidiTimeAxisView::get_device_mode()
813 using namespace MIDI::Name;
815 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
817 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
820 return device_names->custom_device_mode_by_name(
821 gui_property (X_("midnam-custom-device-mode")));
824 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
825 MidiTimeAxisView::get_device_names()
827 using namespace MIDI::Name;
829 const std::string model = gui_property (X_("midnam-model-name"));
831 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
832 .document_by_model(model);
834 return midnam->master_device_names(model);
836 return boost::shared_ptr<MasterDeviceNames>();
841 MidiTimeAxisView::build_controller_menu ()
843 using namespace Menu_Helpers;
845 if (controller_menu) {
846 /* it exists and has not been invalidated by a channel mode change */
850 controller_menu = new Menu; // explicitly managed by us
851 MenuList& items (controller_menu->items());
853 /* create several "top level" menu items for sets of controllers (16 at a
854 time), and populate each one with a submenu for each controller+channel
855 combination covering the currently selected channels for this track
858 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
860 /* count the number of selected channels because we will build a different menu
861 structure if there is more than 1 selected.
865 for (uint8_t chn = 0; chn < 16; chn++) {
866 if (selected_channels & (0x0001 << chn)) {
873 using namespace MIDI::Name;
874 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
876 if (device_names && !device_names->controls().empty()) {
877 /* Controllers names available in midnam file, generate fancy menu */
878 unsigned n_items = 0;
879 unsigned n_groups = 0;
881 /* TODO: This is not correct, should look up the currently applicable ControlNameList
882 and only build a menu for that one. */
883 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
884 l != device_names->controls().end(); ++l) {
885 boost::shared_ptr<ControlNameList> name_list = l->second;
886 Menu* ctl_menu = NULL;
888 for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
889 c != name_list->controls().end();) {
890 const uint16_t ctl = c->second->number();
891 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
892 /* Skip bank select controllers since they're handled specially */
894 /* Create a new submenu */
895 ctl_menu = manage (new Menu);
898 MenuList& ctl_items (ctl_menu->items());
900 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
902 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
907 if (ctl_menu && (++n_items == 16 || c == name_list->controls().end())) {
908 /* Submenu has 16 items or we're done, add it to controller menu and reset */
910 MenuElem(string_compose(_("Controllers %1-%2"),
911 (16 * n_groups), (16 * n_groups) + n_items - 1),
920 /* No controllers names, generate generic numeric menu */
921 for (int i = 0; i < 127; i += 16) {
922 Menu* ctl_menu = manage (new Menu);
923 MenuList& ctl_items (ctl_menu->items());
925 for (int ctl = i; ctl < i+16; ++ctl) {
926 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
927 /* Skip bank select controllers since they're handled specially */
932 add_multi_channel_controller_item(
933 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
935 add_single_channel_controller_item(
936 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
940 /* Add submenu for this block of controllers to controller menu */
942 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
949 MidiTimeAxisView::build_note_mode_menu()
951 using namespace Menu_Helpers;
953 Menu* mode_menu = manage (new Menu);
954 MenuList& items = mode_menu->items();
955 mode_menu->set_name ("ArdourContextMenu");
957 RadioMenuItem::Group mode_group;
959 RadioMenuElem (mode_group,_("Sustained"),
960 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
962 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
963 _note_mode_item->set_active(_note_mode == Sustained);
966 RadioMenuElem (mode_group, _("Percussive"),
967 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
969 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
970 _percussion_mode_item->set_active(_note_mode == Percussive);
976 MidiTimeAxisView::build_color_mode_menu()
978 using namespace Menu_Helpers;
980 Menu* mode_menu = manage (new Menu);
981 MenuList& items = mode_menu->items();
982 mode_menu->set_name ("ArdourContextMenu");
984 RadioMenuItem::Group mode_group;
986 RadioMenuElem (mode_group, _("Meter Colors"),
987 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
988 MeterColors, false, true, true)));
989 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
990 _meter_color_mode_item->set_active(_color_mode == MeterColors);
993 RadioMenuElem (mode_group, _("Channel Colors"),
994 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
995 ChannelColors, false, true, true)));
996 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
997 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
1000 RadioMenuElem (mode_group, _("Track Color"),
1001 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1002 TrackColor, false, true, true)));
1003 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1004 _channel_color_mode_item->set_active(_color_mode == TrackColor);
1010 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
1012 if (apply_to_selection) {
1013 _editor.get_selection().tracks.foreach_midi_time_axis (
1014 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
1016 if (_note_mode != mode || midi_track()->note_mode() != mode) {
1018 midi_track()->set_note_mode(mode);
1019 set_gui_property ("note-mode", enum_2_string(_note_mode));
1020 _view->redisplay_track();
1026 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
1028 if (apply_to_selection) {
1029 _editor.get_selection().tracks.foreach_midi_time_axis (
1030 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
1032 if (_color_mode == mode && !force) {
1036 if (_channel_selector) {
1037 if (mode == ChannelColors) {
1038 _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
1040 _channel_selector->set_default_channel_color();
1045 set_gui_property ("color-mode", enum_2_string(_color_mode));
1047 _view->redisplay_track();
1053 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1055 if (apply_to_selection) {
1056 _editor.get_selection().tracks.foreach_midi_time_axis (
1057 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1059 if (!_ignore_signals) {
1060 midi_view()->set_note_range(range);
1066 MidiTimeAxisView::update_range()
1068 MidiGhostRegion* mgr;
1070 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1071 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1072 mgr->update_range();
1078 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1080 if (apply_to_selection) {
1081 _editor.get_selection().tracks.foreach_midi_time_axis (
1082 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1085 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1087 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1088 create_automation_child(*i, true);
1092 RouteTimeAxisView::show_all_automation ();
1097 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1099 if (apply_to_selection) {
1100 _editor.get_selection().tracks.foreach_midi_time_axis (
1101 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1104 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1106 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1107 create_automation_child (*i, true);
1111 RouteTimeAxisView::show_existing_automation ();
1115 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1118 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1120 if (param.type() == NullAutomation) {
1124 AutomationTracks::iterator existing = _automation_tracks.find (param);
1126 if (existing != _automation_tracks.end()) {
1128 /* automation track created because we had existing data for
1129 * the processor, but visibility may need to be controlled
1130 * since it will have been set visible by default.
1133 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1140 boost::shared_ptr<AutomationTimeAxisView> track;
1142 switch (param.type()) {
1144 case GainAutomation:
1145 create_gain_automation_child (param, show);
1148 case PluginAutomation:
1149 /* handled elsewhere */
1152 case MidiCCAutomation:
1153 case MidiPgmChangeAutomation:
1154 case MidiPitchBenderAutomation:
1155 case MidiChannelPressureAutomation:
1156 case MidiSystemExclusiveAutomation:
1157 /* These controllers are region "automation" - they are owned
1158 * by regions (and their MidiModels), not by the track. As a
1159 * result we do not create an AutomationList/Line for the track
1160 * ... except here we are doing something!! XXX
1163 track.reset (new AutomationTimeAxisView (
1166 boost::shared_ptr<Automatable> (),
1167 boost::shared_ptr<AutomationControl> (),
1173 _route->describe_parameter(param)));
1176 _view->foreach_regionview (
1177 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1180 add_automation_child (param, track, show);
1184 error << "MidiTimeAxisView: unknown automation child "
1185 << EventTypeMap::instance().to_symbol(param) << endmsg;
1190 MidiTimeAxisView::route_active_changed ()
1192 RouteUI::route_active_changed ();
1195 if (_route->active()) {
1196 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1197 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1198 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1200 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1201 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1202 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1205 if (_route->active()) {
1206 controls_ebox.set_name ("BusControlsBaseUnselected");
1207 controls_base_selected_name = "BusControlsBaseSelected";
1208 controls_base_unselected_name = "BusControlsBaseUnselected";
1210 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1211 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1212 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1218 MidiTimeAxisView::set_note_selection (uint8_t note)
1220 if (!_editor.internal_editing()) {
1224 uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1226 if (_view->num_selected_regionviews() == 0) {
1227 _view->foreach_regionview (
1228 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1231 _view->foreach_selected_regionview (
1232 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1238 MidiTimeAxisView::add_note_selection (uint8_t note)
1240 if (!_editor.internal_editing()) {
1244 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1246 if (_view->num_selected_regionviews() == 0) {
1247 _view->foreach_regionview (
1248 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1251 _view->foreach_selected_regionview (
1252 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1258 MidiTimeAxisView::extend_note_selection (uint8_t note)
1260 if (!_editor.internal_editing()) {
1264 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1266 if (_view->num_selected_regionviews() == 0) {
1267 _view->foreach_regionview (
1268 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1271 _view->foreach_selected_regionview (
1272 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1278 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1280 if (!_editor.internal_editing()) {
1284 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1286 if (_view->num_selected_regionviews() == 0) {
1287 _view->foreach_regionview (
1288 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1291 _view->foreach_selected_regionview (
1292 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1298 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1300 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1304 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1306 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1310 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1312 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1316 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1318 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1322 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1324 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1328 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1329 bool changed = false;
1333 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1335 for (uint32_t chn = 0; chn < 16; ++chn) {
1336 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1337 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1343 if ((selected_channels & (0x0001 << chn)) == 0) {
1344 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1345 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1347 changed = track->set_marked_for_display (false) || changed;
1349 changed = track->set_marked_for_display (true) || changed;
1356 /* TODO: Bender, Pressure */
1358 /* invalidate the controller menu, so that we rebuild it next time */
1359 _controller_menu_map.clear ();
1360 delete controller_menu;
1361 controller_menu = 0;
1369 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1371 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1376 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1377 if (i != _controller_menu_map.end()) {
1381 i = _channel_command_menu_map.find (param);
1382 if (i != _channel_command_menu_map.end()) {
1389 boost::shared_ptr<MidiRegion>
1390 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1392 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1394 real_editor->begin_reversible_command (Operations::create_region);
1395 playlist()->clear_changes ();
1397 real_editor->snap_to (pos, 0);
1399 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1400 view()->trackview().track().get(), view()->trackview().track()->name());
1403 plist.add (ARDOUR::Properties::start, 0);
1404 plist.add (ARDOUR::Properties::length, length);
1405 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1407 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1409 playlist()->add_region (region, pos);
1410 _session->add_command (new StatefulDiffCommand (playlist()));
1413 real_editor->commit_reversible_command ();
1416 return boost::dynamic_pointer_cast<MidiRegion>(region);
1420 MidiTimeAxisView::ensure_step_editor ()
1422 if (!_step_editor) {
1423 _step_editor = new StepEditor (_editor, midi_track(), *this);
1428 MidiTimeAxisView::start_step_editing ()
1430 ensure_step_editor ();
1431 _step_editor->start_step_editing ();
1435 MidiTimeAxisView::stop_step_editing ()
1438 _step_editor->stop_step_editing ();
1442 /** @return channel (counted from 0) to add an event to, based on the current setting
1443 * of the channel selector.
1446 MidiTimeAxisView::get_channel_for_add () const
1448 uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
1450 uint8_t channel = 0;
1452 /* pick the highest selected channel, unless all channels are selected,
1453 which is interpreted to mean channel 1 (zero)
1456 for (uint16_t i = 0; i < 16; ++i) {
1457 if (chn_mask & (1<<i)) {
1463 if (chn_cnt == 16) {
1471 MidiTimeAxisView::note_range_changed ()
1473 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1474 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1478 MidiTimeAxisView::contents_height_changed ()
1480 _range_scroomer->set_size_request (-1, _view->child_height ());
1484 MidiTimeAxisView::playback_channel_mode_changed ()
1486 switch (midi_track()->get_playback_channel_mode()) {
1488 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("all")));
1490 case FilterChannels:
1491 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("some")));
1494 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Play"), _("all"), ffs (midi_track()->get_playback_channel_mask())));
1500 MidiTimeAxisView::capture_channel_mode_changed ()
1502 switch (midi_track()->get_capture_channel_mode()) {
1504 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("all")));
1506 case FilterChannels:
1507 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("some")));
1510 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Rec"), _("all"), ffs (midi_track()->get_capture_channel_mask())));