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);
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->set_position (WIN_POS_MOUSE);
521 _channel_selector->show_all ();
523 _channel_selector->cycle_visibility ();
528 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
530 using namespace Menu_Helpers;
532 /* If we have a controller menu, we need to detach it before
533 RouteTimeAxis::build_automation_action_menu destroys the
534 menu it is attached to. Otherwise GTK destroys
535 controller_menu's gobj, meaning that it can't be reattached
536 below. See bug #3134.
539 if (controller_menu) {
540 detach_menu (*controller_menu);
543 _channel_command_menu_map.clear ();
544 RouteTimeAxisView::build_automation_action_menu (for_selection);
546 MenuList& automation_items = automation_action_menu->items();
548 uint16_t selected_channels = midi_track()->get_playback_channel_mask();
550 if (selected_channels != 0) {
552 automation_items.push_back (SeparatorElem());
554 /* these 2 MIDI "command" types are semantically more like automation
555 than note data, but they are not MIDI controllers. We give them
556 special status in this menu, since they will not show up in the
557 controller list and anyone who actually knows something about MIDI
558 (!) would not expect to find them there.
561 add_channel_command_menu_item (
562 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
563 automation_items.back().set_sensitive (
564 !for_selection || _editor.get_selection().tracks.size() == 1);
565 add_channel_command_menu_item (
566 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
567 automation_items.back().set_sensitive (
568 !for_selection || _editor.get_selection().tracks.size() == 1);
570 /* now all MIDI controllers. Always offer the possibility that we will
571 rebuild the controllers menu since it might need to be updated after
572 a channel mode change or other change. Also detach it first in case
573 it has been used anywhere else.
576 build_controller_menu ();
578 automation_items.push_back (SeparatorElem());
579 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
580 automation_items.back().set_sensitive (
581 !for_selection || _editor.get_selection().tracks.size() == 1);
583 automation_items.push_back (
584 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
585 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
590 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
592 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
594 for (uint8_t chn = 0; chn < 16; chn++) {
595 if (selected_channels & (0x0001 << chn)) {
597 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
598 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
601 menu->set_active (yn);
608 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
610 AutomationType auto_type,
613 using namespace Menu_Helpers;
615 /* count the number of selected channels because we will build a different menu
616 structure if there is more than 1 selected.
619 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
622 for (uint8_t chn = 0; chn < 16; chn++) {
623 if (selected_channels & (0x0001 << chn)) {
632 /* multiple channels - create a submenu, with 1 item per channel */
634 Menu* chn_menu = manage (new Menu);
635 MenuList& chn_items (chn_menu->items());
636 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
638 /* add a couple of items to hide/show all of them */
640 chn_items.push_back (
641 MenuElem (_("Hide all channels"),
642 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
643 false, param_without_channel)));
644 chn_items.push_back (
645 MenuElem (_("Show all channels"),
646 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
647 true, param_without_channel)));
649 for (uint8_t chn = 0; chn < 16; chn++) {
650 if (selected_channels & (0x0001 << chn)) {
652 /* for each selected channel, add a menu item for this controller */
654 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
655 chn_items.push_back (
656 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
657 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
658 fully_qualified_param)));
660 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
661 bool visible = false;
664 if (track->marked_for_display()) {
669 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
670 _channel_command_menu_map[fully_qualified_param] = cmi;
671 cmi->set_active (visible);
675 /* now create an item in the parent menu that has the per-channel list as a submenu */
677 items.push_back (MenuElem (label, *chn_menu));
681 /* just one channel - create a single menu item for this command+channel combination*/
683 for (uint8_t chn = 0; chn < 16; chn++) {
684 if (selected_channels & (0x0001 << chn)) {
686 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
688 CheckMenuElem (label,
689 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
690 fully_qualified_param)));
692 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
693 bool visible = false;
696 if (track->marked_for_display()) {
701 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
702 _channel_command_menu_map[fully_qualified_param] = cmi;
703 cmi->set_active (visible);
705 /* one channel only */
712 /** Add a single menu item for a controller on one channel. */
714 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
716 const std::string& name)
718 using namespace Menu_Helpers;
720 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
721 for (uint8_t chn = 0; chn < 16; chn++) {
722 if (selected_channels & (0x0001 << chn)) {
724 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
725 ctl_items.push_back (
727 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
729 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
730 fully_qualified_param)));
731 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
733 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
734 fully_qualified_param);
736 bool visible = false;
738 if (track->marked_for_display()) {
743 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
744 _controller_menu_map[fully_qualified_param] = cmi;
745 cmi->set_active (visible);
747 /* one channel only */
753 /** Add a submenu with 1 item per channel for a controller on many channels. */
755 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
757 const std::string& name)
759 using namespace Menu_Helpers;
761 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
763 Menu* chn_menu = manage (new Menu);
764 MenuList& chn_items (chn_menu->items());
766 /* add a couple of items to hide/show this controller on all channels */
768 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
769 chn_items.push_back (
770 MenuElem (_("Hide all channels"),
771 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
772 false, param_without_channel)));
773 chn_items.push_back (
774 MenuElem (_("Show all channels"),
775 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
776 true, param_without_channel)));
778 for (uint8_t chn = 0; chn < 16; chn++) {
779 if (selected_channels & (0x0001 << chn)) {
781 /* for each selected channel, add a menu item for this controller */
783 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
784 chn_items.push_back (
785 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
786 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
787 fully_qualified_param)));
789 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
790 fully_qualified_param);
791 bool visible = false;
794 if (track->marked_for_display()) {
799 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
800 _controller_menu_map[fully_qualified_param] = cmi;
801 cmi->set_active (visible);
805 /* add the per-channel menu to the list of controllers, with the name of the controller */
806 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
808 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
811 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
812 MidiTimeAxisView::get_device_mode()
814 using namespace MIDI::Name;
816 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
818 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
821 return device_names->custom_device_mode_by_name(
822 gui_property (X_("midnam-custom-device-mode")));
825 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
826 MidiTimeAxisView::get_device_names()
828 using namespace MIDI::Name;
830 const std::string model = gui_property (X_("midnam-model-name"));
832 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
833 .document_by_model(model);
835 return midnam->master_device_names(model);
837 return boost::shared_ptr<MasterDeviceNames>();
842 MidiTimeAxisView::build_controller_menu ()
844 using namespace Menu_Helpers;
846 if (controller_menu) {
847 /* it exists and has not been invalidated by a channel mode change */
851 controller_menu = new Menu; // explicitly managed by us
852 MenuList& items (controller_menu->items());
854 /* create several "top level" menu items for sets of controllers (16 at a
855 time), and populate each one with a submenu for each controller+channel
856 combination covering the currently selected channels for this track
859 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
861 /* count the number of selected channels because we will build a different menu
862 structure if there is more than 1 selected.
866 for (uint8_t chn = 0; chn < 16; chn++) {
867 if (selected_channels & (0x0001 << chn)) {
874 using namespace MIDI::Name;
875 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
877 if (device_names && !device_names->controls().empty()) {
878 /* Controllers names available in midnam file, generate fancy menu */
879 unsigned n_items = 0;
880 unsigned n_groups = 0;
882 /* TODO: This is not correct, should look up the currently applicable ControlNameList
883 and only build a menu for that one. */
884 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
885 l != device_names->controls().end(); ++l) {
886 boost::shared_ptr<ControlNameList> name_list = l->second;
887 Menu* ctl_menu = NULL;
889 for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
890 c != name_list->controls().end();) {
891 const uint16_t ctl = c->second->number();
892 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
893 /* Skip bank select controllers since they're handled specially */
895 /* Create a new submenu */
896 ctl_menu = manage (new Menu);
899 MenuList& ctl_items (ctl_menu->items());
901 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
903 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
908 if (ctl_menu && (++n_items == 16 || c == name_list->controls().end())) {
909 /* Submenu has 16 items or we're done, add it to controller menu and reset */
911 MenuElem(string_compose(_("Controllers %1-%2"),
912 (16 * n_groups), (16 * n_groups) + n_items - 1),
921 /* No controllers names, generate generic numeric menu */
922 for (int i = 0; i < 127; i += 16) {
923 Menu* ctl_menu = manage (new Menu);
924 MenuList& ctl_items (ctl_menu->items());
926 for (int ctl = i; ctl < i+16; ++ctl) {
927 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
928 /* Skip bank select controllers since they're handled specially */
933 add_multi_channel_controller_item(
934 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
936 add_single_channel_controller_item(
937 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
941 /* Add submenu for this block of controllers to controller menu */
943 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
950 MidiTimeAxisView::build_note_mode_menu()
952 using namespace Menu_Helpers;
954 Menu* mode_menu = manage (new Menu);
955 MenuList& items = mode_menu->items();
956 mode_menu->set_name ("ArdourContextMenu");
958 RadioMenuItem::Group mode_group;
960 RadioMenuElem (mode_group,_("Sustained"),
961 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
963 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
964 _note_mode_item->set_active(_note_mode == Sustained);
967 RadioMenuElem (mode_group, _("Percussive"),
968 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
970 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
971 _percussion_mode_item->set_active(_note_mode == Percussive);
977 MidiTimeAxisView::build_color_mode_menu()
979 using namespace Menu_Helpers;
981 Menu* mode_menu = manage (new Menu);
982 MenuList& items = mode_menu->items();
983 mode_menu->set_name ("ArdourContextMenu");
985 RadioMenuItem::Group mode_group;
987 RadioMenuElem (mode_group, _("Meter Colors"),
988 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
989 MeterColors, false, true, true)));
990 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
991 _meter_color_mode_item->set_active(_color_mode == MeterColors);
994 RadioMenuElem (mode_group, _("Channel Colors"),
995 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
996 ChannelColors, false, true, true)));
997 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
998 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
1001 RadioMenuElem (mode_group, _("Track Color"),
1002 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1003 TrackColor, false, true, true)));
1004 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1005 _channel_color_mode_item->set_active(_color_mode == TrackColor);
1011 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
1013 if (apply_to_selection) {
1014 _editor.get_selection().tracks.foreach_midi_time_axis (
1015 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
1017 if (_note_mode != mode || midi_track()->note_mode() != mode) {
1019 midi_track()->set_note_mode(mode);
1020 set_gui_property ("note-mode", enum_2_string(_note_mode));
1021 _view->redisplay_track();
1027 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
1029 if (apply_to_selection) {
1030 _editor.get_selection().tracks.foreach_midi_time_axis (
1031 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
1033 if (_color_mode == mode && !force) {
1037 if (_channel_selector) {
1038 if (mode == ChannelColors) {
1039 _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
1041 _channel_selector->set_default_channel_color();
1046 set_gui_property ("color-mode", enum_2_string(_color_mode));
1048 _view->redisplay_track();
1054 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1056 if (apply_to_selection) {
1057 _editor.get_selection().tracks.foreach_midi_time_axis (
1058 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1060 if (!_ignore_signals) {
1061 midi_view()->set_note_range(range);
1067 MidiTimeAxisView::update_range()
1069 MidiGhostRegion* mgr;
1071 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1072 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1073 mgr->update_range();
1079 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1081 if (apply_to_selection) {
1082 _editor.get_selection().tracks.foreach_midi_time_axis (
1083 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1086 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1088 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1089 create_automation_child(*i, true);
1093 RouteTimeAxisView::show_all_automation ();
1098 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1100 if (apply_to_selection) {
1101 _editor.get_selection().tracks.foreach_midi_time_axis (
1102 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1105 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1107 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1108 create_automation_child (*i, true);
1112 RouteTimeAxisView::show_existing_automation ();
1116 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1119 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1121 if (param.type() == NullAutomation) {
1125 AutomationTracks::iterator existing = _automation_tracks.find (param);
1127 if (existing != _automation_tracks.end()) {
1129 /* automation track created because we had existing data for
1130 * the processor, but visibility may need to be controlled
1131 * since it will have been set visible by default.
1134 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1141 boost::shared_ptr<AutomationTimeAxisView> track;
1143 switch (param.type()) {
1145 case GainAutomation:
1146 create_gain_automation_child (param, show);
1149 case PluginAutomation:
1150 /* handled elsewhere */
1153 case MidiCCAutomation:
1154 case MidiPgmChangeAutomation:
1155 case MidiPitchBenderAutomation:
1156 case MidiChannelPressureAutomation:
1157 case MidiSystemExclusiveAutomation:
1158 /* These controllers are region "automation" - they are owned
1159 * by regions (and their MidiModels), not by the track. As a
1160 * result we do not create an AutomationList/Line for the track
1161 * ... except here we are doing something!! XXX
1164 track.reset (new AutomationTimeAxisView (
1167 boost::shared_ptr<Automatable> (),
1168 boost::shared_ptr<AutomationControl> (),
1174 _route->describe_parameter(param)));
1177 _view->foreach_regionview (
1178 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1181 add_automation_child (param, track, show);
1185 error << "MidiTimeAxisView: unknown automation child "
1186 << EventTypeMap::instance().to_symbol(param) << endmsg;
1191 MidiTimeAxisView::route_active_changed ()
1193 RouteUI::route_active_changed ();
1196 if (_route->active()) {
1197 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1198 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1199 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1201 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1202 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1203 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1206 if (_route->active()) {
1207 controls_ebox.set_name ("BusControlsBaseUnselected");
1208 controls_base_selected_name = "BusControlsBaseSelected";
1209 controls_base_unselected_name = "BusControlsBaseUnselected";
1211 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1212 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1213 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1219 MidiTimeAxisView::set_note_selection (uint8_t note)
1221 if (!_editor.internal_editing()) {
1225 uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1227 if (_view->num_selected_regionviews() == 0) {
1228 _view->foreach_regionview (
1229 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1232 _view->foreach_selected_regionview (
1233 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1239 MidiTimeAxisView::add_note_selection (uint8_t note)
1241 if (!_editor.internal_editing()) {
1245 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1247 if (_view->num_selected_regionviews() == 0) {
1248 _view->foreach_regionview (
1249 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1252 _view->foreach_selected_regionview (
1253 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1259 MidiTimeAxisView::extend_note_selection (uint8_t note)
1261 if (!_editor.internal_editing()) {
1265 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1267 if (_view->num_selected_regionviews() == 0) {
1268 _view->foreach_regionview (
1269 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1272 _view->foreach_selected_regionview (
1273 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1279 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1281 if (!_editor.internal_editing()) {
1285 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1287 if (_view->num_selected_regionviews() == 0) {
1288 _view->foreach_regionview (
1289 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1292 _view->foreach_selected_regionview (
1293 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1299 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1301 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1305 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1307 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1311 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1313 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1317 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1319 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1323 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1325 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1329 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1330 bool changed = false;
1334 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1336 for (uint32_t chn = 0; chn < 16; ++chn) {
1337 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1338 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1344 if ((selected_channels & (0x0001 << chn)) == 0) {
1345 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1346 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1348 changed = track->set_marked_for_display (false) || changed;
1350 changed = track->set_marked_for_display (true) || changed;
1357 /* TODO: Bender, Pressure */
1359 /* invalidate the controller menu, so that we rebuild it next time */
1360 _controller_menu_map.clear ();
1361 delete controller_menu;
1362 controller_menu = 0;
1370 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1372 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1377 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1378 if (i != _controller_menu_map.end()) {
1382 i = _channel_command_menu_map.find (param);
1383 if (i != _channel_command_menu_map.end()) {
1390 boost::shared_ptr<MidiRegion>
1391 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1393 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1395 real_editor->begin_reversible_command (Operations::create_region);
1396 playlist()->clear_changes ();
1398 real_editor->snap_to (pos, 0);
1400 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1401 view()->trackview().track().get(), view()->trackview().track()->name());
1404 plist.add (ARDOUR::Properties::start, 0);
1405 plist.add (ARDOUR::Properties::length, length);
1406 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1408 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1410 playlist()->add_region (region, pos);
1411 _session->add_command (new StatefulDiffCommand (playlist()));
1414 real_editor->commit_reversible_command ();
1417 return boost::dynamic_pointer_cast<MidiRegion>(region);
1421 MidiTimeAxisView::ensure_step_editor ()
1423 if (!_step_editor) {
1424 _step_editor = new StepEditor (_editor, midi_track(), *this);
1429 MidiTimeAxisView::start_step_editing ()
1431 ensure_step_editor ();
1432 _step_editor->start_step_editing ();
1436 MidiTimeAxisView::stop_step_editing ()
1439 _step_editor->stop_step_editing ();
1443 /** @return channel (counted from 0) to add an event to, based on the current setting
1444 * of the channel selector.
1447 MidiTimeAxisView::get_channel_for_add () const
1449 uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
1451 uint8_t channel = 0;
1453 /* pick the highest selected channel, unless all channels are selected,
1454 which is interpreted to mean channel 1 (zero)
1457 for (uint16_t i = 0; i < 16; ++i) {
1458 if (chn_mask & (1<<i)) {
1464 if (chn_cnt == 16) {
1472 MidiTimeAxisView::note_range_changed ()
1474 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1475 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1479 MidiTimeAxisView::contents_height_changed ()
1481 _range_scroomer->set_size_request (-1, _view->child_height ());
1485 MidiTimeAxisView::playback_channel_mode_changed ()
1487 switch (midi_track()->get_playback_channel_mode()) {
1489 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), ("all")));
1491 case FilterChannels:
1492 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), ("some")));
1495 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Play"), ("all"), ffs (midi_track()->get_playback_channel_mask())));
1501 MidiTimeAxisView::capture_channel_mode_changed ()
1503 switch (midi_track()->get_capture_channel_mode()) {
1505 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), ("all")));
1507 case FilterChannels:
1508 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), ("some")));
1511 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Rec"), ("all"), ffs (midi_track()->get_capture_channel_mask())));