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"
63 #include "canvas-note-event.h"
64 #include "canvas_impl.h"
67 #include "ghostregion.h"
68 #include "gui_thread.h"
70 #include "midi_channel_selector.h"
71 #include "midi_scroomer.h"
72 #include "midi_streamview.h"
73 #include "midi_region_view.h"
74 #include "midi_time_axis.h"
75 #include "piano_roll_header.h"
76 #include "playlist_selector.h"
77 #include "plugin_selector.h"
78 #include "plugin_ui.h"
79 #include "point_selection.h"
81 #include "region_view.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "step_editor.h"
85 #include "simplerect.h"
88 #include "ardour/midi_track.h"
92 using namespace ARDOUR;
95 using namespace Gtkmm2ext;
96 using namespace Editing;
98 // Minimum height at which a control is displayed
99 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 140;
100 static const uint32_t KEYBOARD_MIN_HEIGHT = 130;
102 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
103 : AxisView(sess) // virtually inherited
104 , RouteTimeAxisView(ed, sess, canvas)
105 , _ignore_signals(false)
107 , _piano_roll_header(0)
108 , _note_mode(Sustained)
110 , _percussion_mode_item(0)
111 , _color_mode(MeterColors)
112 , _meter_color_mode_item(0)
113 , _channel_color_mode_item(0)
114 , _track_color_mode_item(0)
115 , _channel_selector (0)
116 , _step_edit_item (0)
117 , controller_menu (0)
123 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
127 _view = new MidiStreamView (*this);
130 _piano_roll_header = new PianoRollHeader(*midi_view());
131 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
132 _range_scroomer->DoubleClicked.connect (
133 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
134 MidiStreamView::ContentsRange, false));
137 /* This next call will result in our height being set up, so it must come after
138 the creation of the piano roll / range scroomer as their visibility is set up
141 RouteTimeAxisView::set_route (rt);
143 _view->apply_color (_color, StreamView::RegionColor);
145 subplugin_menu.set_name ("ArdourContextMenu");
147 if (!gui_property ("note-range-min").empty ()) {
148 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
149 atoi (gui_property ("note-range-max").c_str()),
153 midi_view()->NoteRangeChanged.connect (
154 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
155 _view->ContentsHeightChanged.connect (
156 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
158 ignore_toggle = false;
160 if (is_midi_track()) {
161 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
162 _note_mode = midi_track()->note_mode();
163 } else { // MIDI bus (which doesn't exist yet..)
164 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
167 /* map current state of the route */
169 processors_changed (RouteProcessorChange ());
171 _route->processors_changed.connect (*this, invalidator (*this),
172 boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
176 _piano_roll_header->SetNoteSelection.connect (
177 sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
178 _piano_roll_header->AddNoteSelection.connect (
179 sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
180 _piano_roll_header->ExtendNoteSelection.connect (
181 sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
182 _piano_roll_header->ToggleNoteSelection.connect (
183 sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
185 /* Suspend updates of the StreamView during scroomer drags to speed things up */
186 _range_scroomer->DragStarting.connect (
187 sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
188 _range_scroomer->DragFinishing.connect (
189 sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
191 /* Put the scroomer and the keyboard in a VBox with a padding
192 label so that they can be reduced in height for stacked-view
195 VBox* v = manage (new VBox);
196 HBox* h = manage (new HBox);
197 h->pack_start (*_range_scroomer);
198 h->pack_start (*_piano_roll_header);
199 v->pack_start (*h, false, false);
200 v->pack_start (*manage (new Label ("")), true, true);
203 controls_hbox.pack_start(*v, false, false);
205 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
206 controls_base_selected_name = "MidiTrackControlsBaseSelected";
207 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
209 midi_view()->NoteRangeChanged.connect (
210 sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
212 /* ask for notifications of any new RegionViews */
213 _view->RegionViewAdded.connect (
214 sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
216 midi_track()->PlaybackChannelModeChanged.connect (*this, invalidator (*this),
217 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
219 midi_track()->PlaybackChannelMaskChanged.connect (*this, invalidator (*this),
220 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
222 midi_track()->CaptureChannelModeChanged.connect (*this, invalidator (*this),
223 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
225 midi_track()->CaptureChannelMaskChanged.connect (*this, invalidator (*this),
226 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
229 playback_channel_mode_changed ();
230 capture_channel_mode_changed ();
232 if (!_editor.have_idled()) {
233 /* first idle will do what we need */
239 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
241 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
242 for (; m != patch_manager.all_models().end(); ++m) {
243 _midnam_model_selector.append_text(m->c_str());
246 if (gui_property (X_("midnam-model-name")).empty()) {
247 set_gui_property (X_("midnam-model-name"), "Generic");
250 if (gui_property (X_("midnam-custom-device-mode")).empty()) {
251 boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
253 set_gui_property (X_("midnam-custom-device-mode"),
254 *device_names->custom_device_mode_names().begin());
258 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
259 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
261 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
262 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
264 _midi_controls_box.set_homogeneous(false);
265 _midi_controls_box.set_border_width (10);
267 _channel_status_box.set_homogeneous (false);
268 _channel_status_box.set_spacing (6);
270 _channel_selector_button.set_label (_("Chns"));
271 ARDOUR_UI::instance()->set_tip (_channel_selector_button, _("Click to edit channel settings"));
273 /* fixed sized labels to prevent silly nonsense (though obviously,
274 * they cause their own too)
277 _playback_channel_status.set_size_request (65, -1);
278 _capture_channel_status.set_size_request (60, -1);
280 _channel_status_box.pack_start (_playback_channel_status, false, false);
281 _channel_status_box.pack_start (_capture_channel_status, false, false);
282 _channel_status_box.pack_start (_channel_selector_button, false, false);
283 _channel_status_box.show_all ();
285 _channel_selector_button.signal_clicked().connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_channel_selector));
287 _midi_controls_box.pack_start (_channel_status_box, false, false, 10);
289 if (!patch_manager.all_models().empty()) {
291 _midnam_model_selector.set_size_request(22, 30);
292 _midnam_model_selector.set_border_width(2);
293 _midnam_model_selector.show ();
294 _midi_controls_box.pack_start (_midnam_model_selector);
296 _midnam_custom_device_mode_selector.set_size_request(10, 30);
297 _midnam_custom_device_mode_selector.set_border_width(2);
298 _midnam_custom_device_mode_selector.show ();
300 _midi_controls_box.pack_start (_midnam_custom_device_mode_selector);
304 custom_device_mode_changed();
306 _midnam_model_selector.signal_changed().connect(
307 sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
308 _midnam_custom_device_mode_selector.signal_changed().connect(
309 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
311 controls_vbox.pack_start(_midi_controls_box, false, false);
313 const string color_mode = gui_property ("color-mode");
314 if (!color_mode.empty()) {
315 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
316 if (_channel_selector && _color_mode == ChannelColors) {
317 _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
321 set_color_mode (_color_mode, true, false);
323 const string note_mode = gui_property ("note-mode");
324 if (!note_mode.empty()) {
325 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
326 if (_percussion_mode_item) {
327 _percussion_mode_item->set_active (_note_mode == Percussive);
331 /* Look for any GUI object state nodes that represent automation children
332 * that should exist, and create the children.
335 const list<string> gui_ids = gui_object_state().all_ids ();
336 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
339 Evoral::Parameter parameter (0, 0, 0);
341 bool const p = AutomationTimeAxisView::parse_state_id (
342 *i, route_id, has_parameter, parameter);
343 if (p && route_id == _route->id () && has_parameter) {
344 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
345 create_automation_child (parameter, string_is_affirmative (visible));
351 MidiTimeAxisView::first_idle ()
358 MidiTimeAxisView::~MidiTimeAxisView ()
360 delete _channel_selector;
362 delete _piano_roll_header;
363 _piano_roll_header = 0;
365 delete _range_scroomer;
368 delete controller_menu;
373 MidiTimeAxisView::enter_internal_edit_mode ()
376 midi_view()->enter_internal_edit_mode ();
381 MidiTimeAxisView::leave_internal_edit_mode ()
384 midi_view()->leave_internal_edit_mode ();
389 MidiTimeAxisView::check_step_edit ()
391 ensure_step_editor ();
392 _step_editor->check_step_edit ();
396 MidiTimeAxisView::model_changed()
398 const Glib::ustring model = _midnam_model_selector.get_active_text();
399 set_gui_property (X_("midnam-model-name"), model);
401 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
402 .custom_device_mode_names_by_model(model);
404 _midnam_custom_device_mode_selector.clear_items();
406 for (std::list<std::string>::const_iterator i = device_modes.begin();
407 i != device_modes.end(); ++i) {
408 _midnam_custom_device_mode_selector.append_text(*i);
411 _midnam_custom_device_mode_selector.set_active(0);
413 _route->instrument_info().set_external_instrument (
414 _midnam_model_selector.get_active_text(),
415 _midnam_custom_device_mode_selector.get_active_text());
417 // Rebuild controller menu
418 _controller_menu_map.clear ();
419 delete controller_menu;
421 build_automation_action_menu(false);
425 MidiTimeAxisView::custom_device_mode_changed()
427 const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text();
428 set_gui_property (X_("midnam-custom-device-mode"), mode);
429 _route->instrument_info().set_external_instrument (
430 _midnam_model_selector.get_active_text(), mode);
434 MidiTimeAxisView::midi_view()
436 return dynamic_cast<MidiStreamView*>(_view);
440 MidiTimeAxisView::set_height (uint32_t h)
442 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
443 _midi_controls_box.show ();
445 _midi_controls_box.hide();
448 if (h >= KEYBOARD_MIN_HEIGHT) {
449 if (is_track() && _range_scroomer) {
450 _range_scroomer->show();
452 if (is_track() && _piano_roll_header) {
453 _piano_roll_header->show();
456 if (is_track() && _range_scroomer) {
457 _range_scroomer->hide();
459 if (is_track() && _piano_roll_header) {
460 _piano_roll_header->hide();
464 /* We need to do this after changing visibility of our stuff, as it will
465 eventually trigger a call to Editor::reset_controls_layout_width(),
466 which needs to know if we have just shown or hidden a scroomer /
469 RouteTimeAxisView::set_height (h);
473 MidiTimeAxisView::append_extra_display_menu_items ()
475 using namespace Menu_Helpers;
477 MenuList& items = display_menu->items();
480 Menu *range_menu = manage(new Menu);
481 MenuList& range_items = range_menu->items();
482 range_menu->set_name ("ArdourContextMenu");
484 range_items.push_back (
485 MenuElem (_("Show Full Range"),
486 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
487 MidiStreamView::FullRange, true)));
489 range_items.push_back (
490 MenuElem (_("Fit Contents"),
491 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
492 MidiStreamView::ContentsRange, true)));
494 items.push_back (MenuElem (_("Note Range"), *range_menu));
495 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
496 items.push_back (MenuElem (_("Channel Selector"),
497 sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector)));
499 color_mode_menu = build_color_mode_menu();
500 if (color_mode_menu) {
501 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
504 items.push_back (SeparatorElem ());
508 MidiTimeAxisView::toggle_channel_selector ()
510 if (!_channel_selector) {
511 _channel_selector = new MidiChannelSelectorWindow (midi_track());
513 if (_color_mode == ChannelColors) {
514 _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
516 _channel_selector->set_default_channel_color ();
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 Gtk::CheckMenuItem* cmi = static_cast<Gtk::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 Gtk::CheckMenuItem* cmi = static_cast<Gtk::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 Gtk::CheckMenuItem* cmi = static_cast<Gtk::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 Gtk::CheckMenuItem* cmi = static_cast<Gtk::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(CanvasNoteEvent::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"), PBD::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"), PBD::ffs (midi_track()->get_capture_channel_mask())));