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"
29 #include "pbd/stl_delete.h"
30 #include "pbd/whitespace.h"
31 #include "pbd/basename.h"
32 #include "pbd/enumwriter.h"
33 #include "pbd/memento_command.h"
34 #include "pbd/stateful_diff_command.h"
36 #include "gtkmm2ext/gtk_ui.h"
37 #include "gtkmm2ext/selector.h"
38 #include "gtkmm2ext/bindable_button.h"
39 #include "gtkmm2ext/utils.h"
41 #include "ardour/event_type_map.h"
42 #include "ardour/midi_patch_manager.h"
43 #include "ardour/midi_playlist.h"
44 #include "ardour/midi_region.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/midi_track.h"
47 #include "ardour/operations.h"
48 #include "ardour/playlist.h"
49 #include "ardour/region.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/route.h"
52 #include "ardour/session.h"
53 #include "ardour/session_object.h"
54 #include "ardour/source.h"
55 #include "ardour/track.h"
56 #include "ardour/types.h"
58 #include "midi++/names.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_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 = 162;
100 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
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 , _step_edit_item (0)
116 , controller_menu (0)
122 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
126 _view = new MidiStreamView (*this);
129 _piano_roll_header = new PianoRollHeader(*midi_view());
130 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
131 _range_scroomer->DoubleClicked.connect (
132 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
133 MidiStreamView::ContentsRange, false));
136 /* This next call will result in our height being set up, so it must come after
137 the creation of the piano roll / range scroomer as their visibility is set up
140 RouteTimeAxisView::set_route (rt);
142 _view->apply_color (_color, StreamView::RegionColor);
144 subplugin_menu.set_name ("ArdourContextMenu");
146 if (!gui_property ("note-range-min").empty ()) {
147 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
148 atoi (gui_property ("note-range-max").c_str()),
152 midi_view()->NoteRangeChanged.connect (
153 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
154 _view->ContentsHeightChanged.connect (
155 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
157 ignore_toggle = false;
159 if (is_midi_track()) {
160 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
161 _note_mode = midi_track()->note_mode();
162 } else { // MIDI bus (which doesn't exist yet..)
163 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
166 /* map current state of the route */
168 processors_changed (RouteProcessorChange ());
170 _route->processors_changed.connect (*this, invalidator (*this),
171 boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
175 _piano_roll_header->SetNoteSelection.connect (
176 sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
177 _piano_roll_header->AddNoteSelection.connect (
178 sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
179 _piano_roll_header->ExtendNoteSelection.connect (
180 sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
181 _piano_roll_header->ToggleNoteSelection.connect (
182 sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
184 /* Suspend updates of the StreamView during scroomer drags to speed things up */
185 _range_scroomer->DragStarting.connect (
186 sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
187 _range_scroomer->DragFinishing.connect (
188 sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
190 /* Put the scroomer and the keyboard in a VBox with a padding
191 label so that they can be reduced in height for stacked-view
194 VBox* v = manage (new VBox);
195 HBox* h = manage (new HBox);
196 h->pack_start (*_range_scroomer);
197 h->pack_start (*_piano_roll_header);
198 v->pack_start (*h, false, false);
199 v->pack_start (*manage (new Label ("")), true, true);
202 controls_hbox.pack_start(*v);
204 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
205 controls_base_selected_name = "MidiTrackControlsBaseSelected";
206 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
208 midi_view()->NoteRangeChanged.connect (
209 sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
211 /* ask for notifications of any new RegionViews */
212 _view->RegionViewAdded.connect (
213 sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
215 if (!_editor.have_idled()) {
216 /* first idle will do what we need */
223 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
225 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
226 for (; m != patch_manager.all_models().end(); ++m) {
227 _midnam_model_selector.append_text(m->c_str());
230 if (gui_property (X_("midnam-model-name")).empty()) {
231 set_gui_property (X_("midnam-model-name"), "Generic");
234 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
235 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
237 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
238 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
240 _midi_controls_box.set_homogeneous(false);
241 _midi_controls_box.set_border_width (10);
243 if (!patch_manager.all_models().empty()) {
244 _channel_selector.set_border_width(2);
245 _channel_selector.show_all ();
247 _midi_controls_box.resize(3, 2);
248 _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1);
250 _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2);
252 _midnam_model_selector.set_size_request(22, 30);
253 _midnam_model_selector.set_border_width(2);
254 _midnam_model_selector.show ();
255 _midi_controls_box.attach(_midnam_model_selector, 0, 1, 2, 3);
257 _midnam_custom_device_mode_selector.set_size_request(10, 30);
258 _midnam_custom_device_mode_selector.set_border_width(2);
259 _midnam_custom_device_mode_selector.show ();
261 _midi_controls_box.attach(_midnam_custom_device_mode_selector, 0, 1, 3, 4);
263 _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1);
264 _channel_selector.show_all ();
268 custom_device_mode_changed();
270 _midnam_model_selector.signal_changed().connect(
271 sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
272 _midnam_custom_device_mode_selector.signal_changed().connect(
273 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
275 controls_vbox.pack_start(_midi_controls_box, false, false);
277 // restore channel selector settings
278 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(),
279 midi_track()->get_channel_mask());
280 _channel_selector.mode_changed.connect(
281 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
282 _channel_selector.mode_changed.connect(
283 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
285 const string color_mode = gui_property ("color-mode");
286 if (!color_mode.empty()) {
287 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
288 if (_color_mode == ChannelColors) {
289 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
293 set_color_mode (_color_mode, true, false);
295 const string note_mode = gui_property ("note-mode");
296 if (!note_mode.empty()) {
297 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
298 if (_percussion_mode_item) {
299 _percussion_mode_item->set_active (_note_mode == Percussive);
303 /* Look for any GUI object state nodes that represent automation children
304 * that should exist, and create the children.
307 const list<string> gui_ids = gui_object_state().all_ids ();
308 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
311 Evoral::Parameter parameter (0, 0, 0);
313 bool const p = AutomationTimeAxisView::parse_state_id (
314 *i, route_id, has_parameter, parameter);
315 if (p && route_id == _route->id () && has_parameter) {
316 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
317 create_automation_child (parameter, string_is_affirmative (visible));
323 MidiTimeAxisView::first_idle ()
330 MidiTimeAxisView::~MidiTimeAxisView ()
332 delete _piano_roll_header;
333 _piano_roll_header = 0;
335 delete _range_scroomer;
338 delete controller_menu;
343 MidiTimeAxisView::enter_internal_edit_mode ()
346 midi_view()->enter_internal_edit_mode ();
351 MidiTimeAxisView::leave_internal_edit_mode ()
354 midi_view()->leave_internal_edit_mode ();
359 MidiTimeAxisView::check_step_edit ()
361 ensure_step_editor ();
362 _step_editor->check_step_edit ();
366 MidiTimeAxisView::model_changed()
368 const Glib::ustring model = _midnam_model_selector.get_active_text();
369 set_gui_property (X_("midnam-model-name"), model);
371 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
372 .custom_device_mode_names_by_model(model);
374 _midnam_custom_device_mode_selector.clear_items();
376 if (device_modes.size() < 2) {
377 _midnam_custom_device_mode_selector.hide();
379 _midnam_custom_device_mode_selector.show();
380 for (std::list<std::string>::const_iterator i = device_modes.begin();
381 i != device_modes.end(); ++i) {
382 _midnam_custom_device_mode_selector.append_text(*i);
386 _midnam_custom_device_mode_selector.set_active(0);
388 _route->instrument_info().set_external_instrument (
389 _midnam_model_selector.get_active_text(),
390 _midnam_custom_device_mode_selector.get_active_text());
392 // Rebuild controller menu
393 _controller_menu_map.clear ();
394 delete controller_menu;
396 build_automation_action_menu(false);
400 MidiTimeAxisView::custom_device_mode_changed()
402 const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text();
403 set_gui_property (X_("midnam-custom-device-mode"), mode);
404 _route->instrument_info().set_external_instrument (
405 _midnam_model_selector.get_active_text(), mode);
409 MidiTimeAxisView::midi_view()
411 return dynamic_cast<MidiStreamView*>(_view);
415 MidiTimeAxisView::set_height (uint32_t h)
417 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
418 _midi_controls_box.show ();
420 _midi_controls_box.hide();
423 if (h >= KEYBOARD_MIN_HEIGHT) {
424 if (is_track() && _range_scroomer) {
425 _range_scroomer->show();
427 if (is_track() && _piano_roll_header) {
428 _piano_roll_header->show();
431 if (is_track() && _range_scroomer) {
432 _range_scroomer->hide();
434 if (is_track() && _piano_roll_header) {
435 _piano_roll_header->hide();
439 /* We need to do this after changing visibility of our stuff, as it will
440 eventually trigger a call to Editor::reset_controls_layout_width(),
441 which needs to know if we have just shown or hidden a scroomer /
444 RouteTimeAxisView::set_height (h);
448 MidiTimeAxisView::append_extra_display_menu_items ()
450 using namespace Menu_Helpers;
452 MenuList& items = display_menu->items();
455 Menu *range_menu = manage(new Menu);
456 MenuList& range_items = range_menu->items();
457 range_menu->set_name ("ArdourContextMenu");
459 range_items.push_back (
460 MenuElem (_("Show Full Range"),
461 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
462 MidiStreamView::FullRange, true)));
464 range_items.push_back (
465 MenuElem (_("Fit Contents"),
466 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
467 MidiStreamView::ContentsRange, true)));
469 items.push_back (MenuElem (_("Note Range"), *range_menu));
470 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
472 items.push_back (SeparatorElem ());
476 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
478 using namespace Menu_Helpers;
480 /* If we have a controller menu, we need to detach it before
481 RouteTimeAxis::build_automation_action_menu destroys the
482 menu it is attached to. Otherwise GTK destroys
483 controller_menu's gobj, meaning that it can't be reattached
484 below. See bug #3134.
487 if (controller_menu) {
488 detach_menu (*controller_menu);
491 _channel_command_menu_map.clear ();
492 RouteTimeAxisView::build_automation_action_menu (for_selection);
494 MenuList& automation_items = automation_action_menu->items();
496 uint16_t selected_channels = _channel_selector.get_selected_channels();
498 if (selected_channels != 0) {
500 automation_items.push_back (SeparatorElem());
502 /* these 2 MIDI "command" types are semantically more like automation
503 than note data, but they are not MIDI controllers. We give them
504 special status in this menu, since they will not show up in the
505 controller list and anyone who actually knows something about MIDI
506 (!) would not expect to find them there.
509 add_channel_command_menu_item (
510 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
511 automation_items.back().set_sensitive (
512 !for_selection || _editor.get_selection().tracks.size() == 1);
513 add_channel_command_menu_item (
514 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
515 automation_items.back().set_sensitive (
516 !for_selection || _editor.get_selection().tracks.size() == 1);
518 /* now all MIDI controllers. Always offer the possibility that we will
519 rebuild the controllers menu since it might need to be updated after
520 a channel mode change or other change. Also detach it first in case
521 it has been used anywhere else.
524 build_controller_menu ();
526 automation_items.push_back (SeparatorElem());
527 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
528 automation_items.back().set_sensitive (
529 !for_selection || _editor.get_selection().tracks.size() == 1);
531 automation_items.push_back (
532 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
533 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
538 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
540 const uint16_t selected_channels = _channel_selector.get_selected_channels();
542 for (uint8_t chn = 0; chn < 16; chn++) {
543 if (selected_channels & (0x0001 << chn)) {
545 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
546 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
549 menu->set_active (yn);
556 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
558 AutomationType auto_type,
561 using namespace Menu_Helpers;
563 /* count the number of selected channels because we will build a different menu
564 structure if there is more than 1 selected.
567 const uint16_t selected_channels = _channel_selector.get_selected_channels();
570 for (uint8_t chn = 0; chn < 16; chn++) {
571 if (selected_channels & (0x0001 << chn)) {
580 /* multiple channels - create a submenu, with 1 item per channel */
582 Menu* chn_menu = manage (new Menu);
583 MenuList& chn_items (chn_menu->items());
584 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
586 /* add a couple of items to hide/show all of them */
588 chn_items.push_back (
589 MenuElem (_("Hide all channels"),
590 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
591 false, param_without_channel)));
592 chn_items.push_back (
593 MenuElem (_("Show all channels"),
594 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
595 true, param_without_channel)));
597 for (uint8_t chn = 0; chn < 16; chn++) {
598 if (selected_channels & (0x0001 << chn)) {
600 /* for each selected channel, add a menu item for this controller */
602 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
603 chn_items.push_back (
604 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
605 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
606 fully_qualified_param)));
608 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
609 bool visible = false;
612 if (track->marked_for_display()) {
617 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
618 _channel_command_menu_map[fully_qualified_param] = cmi;
619 cmi->set_active (visible);
623 /* now create an item in the parent menu that has the per-channel list as a submenu */
625 items.push_back (MenuElem (label, *chn_menu));
629 /* just one channel - create a single menu item for this command+channel combination*/
631 for (uint8_t chn = 0; chn < 16; chn++) {
632 if (selected_channels & (0x0001 << chn)) {
634 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
636 CheckMenuElem (label,
637 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
638 fully_qualified_param)));
640 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
641 bool visible = false;
644 if (track->marked_for_display()) {
649 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
650 _channel_command_menu_map[fully_qualified_param] = cmi;
651 cmi->set_active (visible);
653 /* one channel only */
660 /** Add a single menu item for a controller on one channel. */
662 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
664 const std::string& name)
666 using namespace Menu_Helpers;
668 const uint16_t selected_channels = _channel_selector.get_selected_channels();
669 for (uint8_t chn = 0; chn < 16; chn++) {
670 if (selected_channels & (0x0001 << chn)) {
672 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
673 ctl_items.push_back (
675 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
677 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
678 fully_qualified_param)));
679 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
681 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
682 fully_qualified_param);
684 bool visible = false;
686 if (track->marked_for_display()) {
691 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
692 _controller_menu_map[fully_qualified_param] = cmi;
693 cmi->set_active (visible);
695 /* one channel only */
701 /** Add a submenu with 1 item per channel for a controller on many channels. */
703 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
705 const std::string& name)
707 using namespace Menu_Helpers;
709 const uint16_t selected_channels = _channel_selector.get_selected_channels();
711 Menu* chn_menu = manage (new Menu);
712 MenuList& chn_items (chn_menu->items());
714 /* add a couple of items to hide/show this controller on all channels */
716 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
717 chn_items.push_back (
718 MenuElem (_("Hide all channels"),
719 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
720 false, param_without_channel)));
721 chn_items.push_back (
722 MenuElem (_("Show all channels"),
723 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
724 true, param_without_channel)));
726 for (uint8_t chn = 0; chn < 16; chn++) {
727 if (selected_channels & (0x0001 << chn)) {
729 /* for each selected channel, add a menu item for this controller */
731 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
732 chn_items.push_back (
733 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
734 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
735 fully_qualified_param)));
737 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
738 fully_qualified_param);
739 bool visible = false;
742 if (track->marked_for_display()) {
747 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
748 _controller_menu_map[fully_qualified_param] = cmi;
749 cmi->set_active (visible);
753 /* add the per-channel menu to the list of controllers, with the name of the controller */
754 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
756 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
760 MidiTimeAxisView::build_controller_menu ()
762 using namespace Menu_Helpers;
764 if (controller_menu) {
765 /* it exists and has not been invalidated by a channel mode change */
769 controller_menu = new Menu; // explicitly managed by us
770 MenuList& items (controller_menu->items());
772 /* create several "top level" menu items for sets of controllers (16 at a
773 time), and populate each one with a submenu for each controller+channel
774 combination covering the currently selected channels for this track
777 const uint16_t selected_channels = _channel_selector.get_selected_channels();
779 /* count the number of selected channels because we will build a different menu
780 structure if there is more than 1 selected.
784 for (uint8_t chn = 0; chn < 16; chn++) {
785 if (selected_channels & (0x0001 << chn)) {
792 using namespace MIDI::Name;
793 const Glib::ustring model = _midnam_model_selector.get_active_text();
794 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
795 .document_by_model(model);
796 boost::shared_ptr<MasterDeviceNames> device_names;
799 device_names = midnam->master_device_names(model);
802 if (device_names && !device_names->controls().empty()) {
803 /* Controllers names available in midnam file, generate fancy menu */
804 unsigned n_items = 0;
805 unsigned n_groups = 0;
806 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
807 l != device_names->controls().end(); ++l) {
808 boost::shared_ptr<ControlNameList> name_list = *l;
809 Menu* ctl_menu = NULL;
811 for (ControlNameList::Controls::const_iterator c = (*l)->controls().begin();
812 c != (*l)->controls().end(); ++c) {
813 const int ctl = atoi((*c)->number().c_str());
814 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
815 /* Skip bank select controllers since they're handled specially */
820 /* Create a new submenu */
821 ctl_menu = manage (new Menu);
824 MenuList& ctl_items (ctl_menu->items());
826 add_multi_channel_controller_item(ctl_items, ctl, (*c)->name());
828 add_single_channel_controller_item(ctl_items, ctl, (*c)->name());
831 if (++n_items == 16 || c == (*l)->controls().end()) {
832 /* Submenu has 16 items, add it to controller menu and reset */
834 MenuElem(string_compose(_("Controllers %1-%2"),
835 (16 * n_groups), (16 * n_groups) + n_items - 1),
844 /* No controllers names, generate generic numeric menu */
845 for (int i = 0; i < 127; i += 16) {
846 Menu* ctl_menu = manage (new Menu);
847 MenuList& ctl_items (ctl_menu->items());
849 for (int ctl = i; ctl < i+16; ++ctl) {
850 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
851 /* Skip bank select controllers since they're handled specially */
856 add_multi_channel_controller_item(
857 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
859 add_single_channel_controller_item(
860 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
864 /* Add submenu for this block of controllers to controller menu */
866 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
873 MidiTimeAxisView::build_note_mode_menu()
875 using namespace Menu_Helpers;
877 Menu* mode_menu = manage (new Menu);
878 MenuList& items = mode_menu->items();
879 mode_menu->set_name ("ArdourContextMenu");
881 RadioMenuItem::Group mode_group;
883 RadioMenuElem (mode_group,_("Sustained"),
884 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
886 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
887 _note_mode_item->set_active(_note_mode == Sustained);
890 RadioMenuElem (mode_group, _("Percussive"),
891 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
893 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
894 _percussion_mode_item->set_active(_note_mode == Percussive);
900 MidiTimeAxisView::build_color_mode_menu()
902 using namespace Menu_Helpers;
904 Menu* mode_menu = manage (new Menu);
905 MenuList& items = mode_menu->items();
906 mode_menu->set_name ("ArdourContextMenu");
908 RadioMenuItem::Group mode_group;
910 RadioMenuElem (mode_group, _("Meter Colors"),
911 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
912 MeterColors, false, true, true)));
913 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
914 _meter_color_mode_item->set_active(_color_mode == MeterColors);
917 RadioMenuElem (mode_group, _("Channel Colors"),
918 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
919 ChannelColors, false, true, true)));
920 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
921 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
924 RadioMenuElem (mode_group, _("Track Color"),
925 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
926 TrackColor, false, true, true)));
927 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
928 _channel_color_mode_item->set_active(_color_mode == TrackColor);
934 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
936 if (apply_to_selection) {
937 _editor.get_selection().tracks.foreach_midi_time_axis (
938 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
940 if (_note_mode != mode || midi_track()->note_mode() != mode) {
942 midi_track()->set_note_mode(mode);
943 set_gui_property ("note-mode", enum_2_string(_note_mode));
944 _view->redisplay_track();
950 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
952 if (apply_to_selection) {
953 _editor.get_selection().tracks.foreach_midi_time_axis (
954 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
956 if (_color_mode == mode && !force) {
960 if (mode == ChannelColors) {
961 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
963 _channel_selector.set_default_channel_color();
967 set_gui_property ("color-mode", enum_2_string(_color_mode));
969 _view->redisplay_track();
975 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
977 if (apply_to_selection) {
978 _editor.get_selection().tracks.foreach_midi_time_axis (
979 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
981 if (!_ignore_signals) {
982 midi_view()->set_note_range(range);
988 MidiTimeAxisView::update_range()
990 MidiGhostRegion* mgr;
992 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
993 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1000 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1002 if (apply_to_selection) {
1003 _editor.get_selection().tracks.foreach_midi_time_axis (
1004 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1007 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1009 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1010 create_automation_child(*i, true);
1014 RouteTimeAxisView::show_all_automation ();
1019 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1021 if (apply_to_selection) {
1022 _editor.get_selection().tracks.foreach_midi_time_axis (
1023 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1026 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1028 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1029 create_automation_child (*i, true);
1033 RouteTimeAxisView::show_existing_automation ();
1037 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1040 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1042 if (param.type() == NullAutomation) {
1046 AutomationTracks::iterator existing = _automation_tracks.find (param);
1048 if (existing != _automation_tracks.end()) {
1050 /* automation track created because we had existing data for
1051 * the processor, but visibility may need to be controlled
1052 * since it will have been set visible by default.
1055 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1062 boost::shared_ptr<AutomationTimeAxisView> track;
1064 switch (param.type()) {
1066 case GainAutomation:
1067 create_gain_automation_child (param, show);
1070 case PluginAutomation:
1071 /* handled elsewhere */
1074 case MidiCCAutomation:
1075 case MidiPgmChangeAutomation:
1076 case MidiPitchBenderAutomation:
1077 case MidiChannelPressureAutomation:
1078 case MidiSystemExclusiveAutomation:
1079 /* These controllers are region "automation" - they are owned
1080 * by regions (and their MidiModels), not by the track. As a
1081 * result we do not create an AutomationList/Line for the track
1082 * ... except here we are doing something!! XXX
1085 track.reset (new AutomationTimeAxisView (
1088 boost::shared_ptr<Automatable> (),
1089 boost::shared_ptr<AutomationControl> (),
1095 _route->describe_parameter(param)));
1098 _view->foreach_regionview (
1099 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1102 add_automation_child (param, track, show);
1106 error << "MidiTimeAxisView: unknown automation child "
1107 << EventTypeMap::instance().to_symbol(param) << endmsg;
1112 MidiTimeAxisView::route_active_changed ()
1114 RouteUI::route_active_changed ();
1117 if (_route->active()) {
1118 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1119 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1120 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1122 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1123 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1124 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1127 if (_route->active()) {
1128 controls_ebox.set_name ("BusControlsBaseUnselected");
1129 controls_base_selected_name = "BusControlsBaseSelected";
1130 controls_base_unselected_name = "BusControlsBaseUnselected";
1132 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1133 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1134 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1140 MidiTimeAxisView::set_note_selection (uint8_t note)
1142 if (!_editor.internal_editing()) {
1146 uint16_t chn_mask = _channel_selector.get_selected_channels();
1148 if (_view->num_selected_regionviews() == 0) {
1149 _view->foreach_regionview (
1150 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1153 _view->foreach_selected_regionview (
1154 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1160 MidiTimeAxisView::add_note_selection (uint8_t note)
1162 if (!_editor.internal_editing()) {
1166 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1168 if (_view->num_selected_regionviews() == 0) {
1169 _view->foreach_regionview (
1170 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1173 _view->foreach_selected_regionview (
1174 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1180 MidiTimeAxisView::extend_note_selection (uint8_t note)
1182 if (!_editor.internal_editing()) {
1186 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1188 if (_view->num_selected_regionviews() == 0) {
1189 _view->foreach_regionview (
1190 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1193 _view->foreach_selected_regionview (
1194 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1200 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1202 if (!_editor.internal_editing()) {
1206 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1208 if (_view->num_selected_regionviews() == 0) {
1209 _view->foreach_regionview (
1210 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1213 _view->foreach_selected_regionview (
1214 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1220 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1222 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1226 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1228 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1232 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1234 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1238 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1240 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1244 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1246 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1250 const uint16_t selected_channels = _channel_selector.get_selected_channels();
1251 bool changed = false;
1255 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1257 for (uint32_t chn = 0; chn < 16; ++chn) {
1258 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1259 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1265 if ((selected_channels & (0x0001 << chn)) == 0) {
1266 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1267 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1269 changed = track->set_marked_for_display (false) || changed;
1271 changed = track->set_marked_for_display (true) || changed;
1278 /* TODO: Bender, Pressure */
1280 /* invalidate the controller menu, so that we rebuild it next time */
1281 _controller_menu_map.clear ();
1282 delete controller_menu;
1283 controller_menu = 0;
1291 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1293 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1298 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1299 if (i != _controller_menu_map.end()) {
1303 i = _channel_command_menu_map.find (param);
1304 if (i != _channel_command_menu_map.end()) {
1311 boost::shared_ptr<MidiRegion>
1312 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1314 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1316 real_editor->begin_reversible_command (Operations::create_region);
1317 playlist()->clear_changes ();
1319 real_editor->snap_to (pos, 0);
1321 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1322 view()->trackview().track().get(), view()->trackview().track()->name());
1325 plist.add (ARDOUR::Properties::start, 0);
1326 plist.add (ARDOUR::Properties::length, length);
1327 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1329 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1331 playlist()->add_region (region, pos);
1332 _session->add_command (new StatefulDiffCommand (playlist()));
1335 real_editor->commit_reversible_command ();
1338 return boost::dynamic_pointer_cast<MidiRegion>(region);
1342 MidiTimeAxisView::ensure_step_editor ()
1344 if (!_step_editor) {
1345 _step_editor = new StepEditor (_editor, midi_track(), *this);
1350 MidiTimeAxisView::start_step_editing ()
1352 ensure_step_editor ();
1353 _step_editor->start_step_editing ();
1357 MidiTimeAxisView::stop_step_editing ()
1360 _step_editor->stop_step_editing ();
1365 /** @return channel (counted from 0) to add an event to, based on the current setting
1366 * of the channel selector.
1369 MidiTimeAxisView::get_channel_for_add () const
1371 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1373 uint8_t channel = 0;
1375 /* pick the highest selected channel, unless all channels are selected,
1376 which is interpreted to mean channel 1 (zero)
1379 for (uint16_t i = 0; i < 16; ++i) {
1380 if (chn_mask & (1<<i)) {
1386 if (chn_cnt == 16) {
1394 MidiTimeAxisView::note_range_changed ()
1396 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1397 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1401 MidiTimeAxisView::contents_height_changed ()
1403 _range_scroomer->set_size_request (-1, _view->child_height ());