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 if (gui_property (X_("midnam-custom-device-mode")).empty()) {
235 boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
237 set_gui_property (X_("midnam-custom-device-mode"),
238 *device_names->custom_device_mode_names().begin());
242 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
243 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
245 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
246 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
248 _midi_controls_box.set_homogeneous(false);
249 _midi_controls_box.set_border_width (10);
251 if (!patch_manager.all_models().empty()) {
252 _channel_selector.set_border_width(2);
253 _channel_selector.show_all ();
255 _midi_controls_box.resize(3, 2);
256 _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1);
258 _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2);
260 _midnam_model_selector.set_size_request(22, 30);
261 _midnam_model_selector.set_border_width(2);
262 _midnam_model_selector.show ();
263 _midi_controls_box.attach(_midnam_model_selector, 0, 1, 2, 3);
265 _midnam_custom_device_mode_selector.set_size_request(10, 30);
266 _midnam_custom_device_mode_selector.set_border_width(2);
267 _midnam_custom_device_mode_selector.show ();
269 _midi_controls_box.attach(_midnam_custom_device_mode_selector, 0, 1, 3, 4);
271 _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1);
272 _channel_selector.show_all ();
276 custom_device_mode_changed();
278 _midnam_model_selector.signal_changed().connect(
279 sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
280 _midnam_custom_device_mode_selector.signal_changed().connect(
281 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
283 controls_vbox.pack_start(_midi_controls_box, false, false);
285 // restore channel selector settings
286 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(),
287 midi_track()->get_channel_mask());
288 _channel_selector.mode_changed.connect(
289 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
290 _channel_selector.mode_changed.connect(
291 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
293 const string color_mode = gui_property ("color-mode");
294 if (!color_mode.empty()) {
295 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
296 if (_color_mode == ChannelColors) {
297 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
301 set_color_mode (_color_mode, true, false);
303 const string note_mode = gui_property ("note-mode");
304 if (!note_mode.empty()) {
305 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
306 if (_percussion_mode_item) {
307 _percussion_mode_item->set_active (_note_mode == Percussive);
311 /* Look for any GUI object state nodes that represent automation children
312 * that should exist, and create the children.
315 const list<string> gui_ids = gui_object_state().all_ids ();
316 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
319 Evoral::Parameter parameter (0, 0, 0);
321 bool const p = AutomationTimeAxisView::parse_state_id (
322 *i, route_id, has_parameter, parameter);
323 if (p && route_id == _route->id () && has_parameter) {
324 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
325 create_automation_child (parameter, string_is_affirmative (visible));
331 MidiTimeAxisView::first_idle ()
338 MidiTimeAxisView::~MidiTimeAxisView ()
340 delete _piano_roll_header;
341 _piano_roll_header = 0;
343 delete _range_scroomer;
346 delete controller_menu;
351 MidiTimeAxisView::enter_internal_edit_mode ()
354 midi_view()->enter_internal_edit_mode ();
359 MidiTimeAxisView::leave_internal_edit_mode ()
362 midi_view()->leave_internal_edit_mode ();
367 MidiTimeAxisView::check_step_edit ()
369 ensure_step_editor ();
370 _step_editor->check_step_edit ();
374 MidiTimeAxisView::model_changed()
376 const Glib::ustring model = _midnam_model_selector.get_active_text();
377 set_gui_property (X_("midnam-model-name"), model);
379 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
380 .custom_device_mode_names_by_model(model);
382 _midnam_custom_device_mode_selector.clear_items();
384 for (std::list<std::string>::const_iterator i = device_modes.begin();
385 i != device_modes.end(); ++i) {
386 _midnam_custom_device_mode_selector.append_text(*i);
389 _midnam_custom_device_mode_selector.set_active(0);
391 _route->instrument_info().set_external_instrument (
392 _midnam_model_selector.get_active_text(),
393 _midnam_custom_device_mode_selector.get_active_text());
395 // Rebuild controller menu
396 _controller_menu_map.clear ();
397 delete controller_menu;
399 build_automation_action_menu(false);
403 MidiTimeAxisView::custom_device_mode_changed()
405 const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text();
406 set_gui_property (X_("midnam-custom-device-mode"), mode);
407 _route->instrument_info().set_external_instrument (
408 _midnam_model_selector.get_active_text(), mode);
412 MidiTimeAxisView::midi_view()
414 return dynamic_cast<MidiStreamView*>(_view);
418 MidiTimeAxisView::set_height (uint32_t h)
420 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
421 _midi_controls_box.show ();
423 _midi_controls_box.hide();
426 if (h >= KEYBOARD_MIN_HEIGHT) {
427 if (is_track() && _range_scroomer) {
428 _range_scroomer->show();
430 if (is_track() && _piano_roll_header) {
431 _piano_roll_header->show();
434 if (is_track() && _range_scroomer) {
435 _range_scroomer->hide();
437 if (is_track() && _piano_roll_header) {
438 _piano_roll_header->hide();
442 /* We need to do this after changing visibility of our stuff, as it will
443 eventually trigger a call to Editor::reset_controls_layout_width(),
444 which needs to know if we have just shown or hidden a scroomer /
447 RouteTimeAxisView::set_height (h);
451 MidiTimeAxisView::append_extra_display_menu_items ()
453 using namespace Menu_Helpers;
455 MenuList& items = display_menu->items();
458 Menu *range_menu = manage(new Menu);
459 MenuList& range_items = range_menu->items();
460 range_menu->set_name ("ArdourContextMenu");
462 range_items.push_back (
463 MenuElem (_("Show Full Range"),
464 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
465 MidiStreamView::FullRange, true)));
467 range_items.push_back (
468 MenuElem (_("Fit Contents"),
469 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
470 MidiStreamView::ContentsRange, true)));
472 items.push_back (MenuElem (_("Note Range"), *range_menu));
473 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
475 items.push_back (SeparatorElem ());
479 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
481 using namespace Menu_Helpers;
483 /* If we have a controller menu, we need to detach it before
484 RouteTimeAxis::build_automation_action_menu destroys the
485 menu it is attached to. Otherwise GTK destroys
486 controller_menu's gobj, meaning that it can't be reattached
487 below. See bug #3134.
490 if (controller_menu) {
491 detach_menu (*controller_menu);
494 _channel_command_menu_map.clear ();
495 RouteTimeAxisView::build_automation_action_menu (for_selection);
497 MenuList& automation_items = automation_action_menu->items();
499 uint16_t selected_channels = _channel_selector.get_selected_channels();
501 if (selected_channels != 0) {
503 automation_items.push_back (SeparatorElem());
505 /* these 2 MIDI "command" types are semantically more like automation
506 than note data, but they are not MIDI controllers. We give them
507 special status in this menu, since they will not show up in the
508 controller list and anyone who actually knows something about MIDI
509 (!) would not expect to find them there.
512 add_channel_command_menu_item (
513 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
514 automation_items.back().set_sensitive (
515 !for_selection || _editor.get_selection().tracks.size() == 1);
516 add_channel_command_menu_item (
517 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
518 automation_items.back().set_sensitive (
519 !for_selection || _editor.get_selection().tracks.size() == 1);
521 /* now all MIDI controllers. Always offer the possibility that we will
522 rebuild the controllers menu since it might need to be updated after
523 a channel mode change or other change. Also detach it first in case
524 it has been used anywhere else.
527 build_controller_menu ();
529 automation_items.push_back (SeparatorElem());
530 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
531 automation_items.back().set_sensitive (
532 !for_selection || _editor.get_selection().tracks.size() == 1);
534 automation_items.push_back (
535 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
536 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
541 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
543 const uint16_t selected_channels = _channel_selector.get_selected_channels();
545 for (uint8_t chn = 0; chn < 16; chn++) {
546 if (selected_channels & (0x0001 << chn)) {
548 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
549 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
552 menu->set_active (yn);
559 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
561 AutomationType auto_type,
564 using namespace Menu_Helpers;
566 /* count the number of selected channels because we will build a different menu
567 structure if there is more than 1 selected.
570 const uint16_t selected_channels = _channel_selector.get_selected_channels();
573 for (uint8_t chn = 0; chn < 16; chn++) {
574 if (selected_channels & (0x0001 << chn)) {
583 /* multiple channels - create a submenu, with 1 item per channel */
585 Menu* chn_menu = manage (new Menu);
586 MenuList& chn_items (chn_menu->items());
587 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
589 /* add a couple of items to hide/show all of them */
591 chn_items.push_back (
592 MenuElem (_("Hide all channels"),
593 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
594 false, param_without_channel)));
595 chn_items.push_back (
596 MenuElem (_("Show all channels"),
597 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
598 true, param_without_channel)));
600 for (uint8_t chn = 0; chn < 16; chn++) {
601 if (selected_channels & (0x0001 << chn)) {
603 /* for each selected channel, add a menu item for this controller */
605 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
606 chn_items.push_back (
607 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
608 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
609 fully_qualified_param)));
611 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
612 bool visible = false;
615 if (track->marked_for_display()) {
620 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
621 _channel_command_menu_map[fully_qualified_param] = cmi;
622 cmi->set_active (visible);
626 /* now create an item in the parent menu that has the per-channel list as a submenu */
628 items.push_back (MenuElem (label, *chn_menu));
632 /* just one channel - create a single menu item for this command+channel combination*/
634 for (uint8_t chn = 0; chn < 16; chn++) {
635 if (selected_channels & (0x0001 << chn)) {
637 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
639 CheckMenuElem (label,
640 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
641 fully_qualified_param)));
643 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
644 bool visible = false;
647 if (track->marked_for_display()) {
652 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
653 _channel_command_menu_map[fully_qualified_param] = cmi;
654 cmi->set_active (visible);
656 /* one channel only */
663 /** Add a single menu item for a controller on one channel. */
665 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
667 const std::string& name)
669 using namespace Menu_Helpers;
671 const uint16_t selected_channels = _channel_selector.get_selected_channels();
672 for (uint8_t chn = 0; chn < 16; chn++) {
673 if (selected_channels & (0x0001 << chn)) {
675 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
676 ctl_items.push_back (
678 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
680 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
681 fully_qualified_param)));
682 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
684 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
685 fully_qualified_param);
687 bool visible = false;
689 if (track->marked_for_display()) {
694 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
695 _controller_menu_map[fully_qualified_param] = cmi;
696 cmi->set_active (visible);
698 /* one channel only */
704 /** Add a submenu with 1 item per channel for a controller on many channels. */
706 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
708 const std::string& name)
710 using namespace Menu_Helpers;
712 const uint16_t selected_channels = _channel_selector.get_selected_channels();
714 Menu* chn_menu = manage (new Menu);
715 MenuList& chn_items (chn_menu->items());
717 /* add a couple of items to hide/show this controller on all channels */
719 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
720 chn_items.push_back (
721 MenuElem (_("Hide all channels"),
722 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
723 false, param_without_channel)));
724 chn_items.push_back (
725 MenuElem (_("Show all channels"),
726 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
727 true, param_without_channel)));
729 for (uint8_t chn = 0; chn < 16; chn++) {
730 if (selected_channels & (0x0001 << chn)) {
732 /* for each selected channel, add a menu item for this controller */
734 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
735 chn_items.push_back (
736 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
737 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
738 fully_qualified_param)));
740 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
741 fully_qualified_param);
742 bool visible = false;
745 if (track->marked_for_display()) {
750 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
751 _controller_menu_map[fully_qualified_param] = cmi;
752 cmi->set_active (visible);
756 /* add the per-channel menu to the list of controllers, with the name of the controller */
757 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
759 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
762 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
763 MidiTimeAxisView::get_device_mode()
765 using namespace MIDI::Name;
767 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
769 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
772 return device_names->custom_device_mode_by_name(
773 gui_property (X_("midnam-custom-device-mode")));
776 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
777 MidiTimeAxisView::get_device_names()
779 using namespace MIDI::Name;
781 const std::string model = gui_property (X_("midnam-model-name"));
783 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
784 .document_by_model(model);
786 return midnam->master_device_names(model);
788 return boost::shared_ptr<MasterDeviceNames>();
793 MidiTimeAxisView::build_controller_menu ()
795 using namespace Menu_Helpers;
797 if (controller_menu) {
798 /* it exists and has not been invalidated by a channel mode change */
802 controller_menu = new Menu; // explicitly managed by us
803 MenuList& items (controller_menu->items());
805 /* create several "top level" menu items for sets of controllers (16 at a
806 time), and populate each one with a submenu for each controller+channel
807 combination covering the currently selected channels for this track
810 const uint16_t selected_channels = _channel_selector.get_selected_channels();
812 /* count the number of selected channels because we will build a different menu
813 structure if there is more than 1 selected.
817 for (uint8_t chn = 0; chn < 16; chn++) {
818 if (selected_channels & (0x0001 << chn)) {
825 using namespace MIDI::Name;
826 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
828 if (device_names && !device_names->controls().empty()) {
829 /* Controllers names available in midnam file, generate fancy menu */
830 unsigned n_items = 0;
831 unsigned n_groups = 0;
832 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
833 l != device_names->controls().end(); ++l) {
834 boost::shared_ptr<ControlNameList> name_list = *l;
835 Menu* ctl_menu = NULL;
837 for (ControlNameList::Controls::const_iterator c = (*l)->controls().begin();
838 c != (*l)->controls().end(); ++c) {
839 const int ctl = atoi((*c)->number().c_str());
840 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
841 /* Skip bank select controllers since they're handled specially */
846 /* Create a new submenu */
847 ctl_menu = manage (new Menu);
850 MenuList& ctl_items (ctl_menu->items());
852 add_multi_channel_controller_item(ctl_items, ctl, (*c)->name());
854 add_single_channel_controller_item(ctl_items, ctl, (*c)->name());
857 if (++n_items == 16 || c == (*l)->controls().end()) {
858 /* Submenu has 16 items, add it to controller menu and reset */
860 MenuElem(string_compose(_("Controllers %1-%2"),
861 (16 * n_groups), (16 * n_groups) + n_items - 1),
870 /* No controllers names, generate generic numeric menu */
871 for (int i = 0; i < 127; i += 16) {
872 Menu* ctl_menu = manage (new Menu);
873 MenuList& ctl_items (ctl_menu->items());
875 for (int ctl = i; ctl < i+16; ++ctl) {
876 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
877 /* Skip bank select controllers since they're handled specially */
882 add_multi_channel_controller_item(
883 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
885 add_single_channel_controller_item(
886 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
890 /* Add submenu for this block of controllers to controller menu */
892 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
899 MidiTimeAxisView::build_note_mode_menu()
901 using namespace Menu_Helpers;
903 Menu* mode_menu = manage (new Menu);
904 MenuList& items = mode_menu->items();
905 mode_menu->set_name ("ArdourContextMenu");
907 RadioMenuItem::Group mode_group;
909 RadioMenuElem (mode_group,_("Sustained"),
910 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
912 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
913 _note_mode_item->set_active(_note_mode == Sustained);
916 RadioMenuElem (mode_group, _("Percussive"),
917 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
919 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
920 _percussion_mode_item->set_active(_note_mode == Percussive);
926 MidiTimeAxisView::build_color_mode_menu()
928 using namespace Menu_Helpers;
930 Menu* mode_menu = manage (new Menu);
931 MenuList& items = mode_menu->items();
932 mode_menu->set_name ("ArdourContextMenu");
934 RadioMenuItem::Group mode_group;
936 RadioMenuElem (mode_group, _("Meter Colors"),
937 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
938 MeterColors, false, true, true)));
939 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
940 _meter_color_mode_item->set_active(_color_mode == MeterColors);
943 RadioMenuElem (mode_group, _("Channel Colors"),
944 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
945 ChannelColors, false, true, true)));
946 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
947 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
950 RadioMenuElem (mode_group, _("Track Color"),
951 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
952 TrackColor, false, true, true)));
953 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
954 _channel_color_mode_item->set_active(_color_mode == TrackColor);
960 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
962 if (apply_to_selection) {
963 _editor.get_selection().tracks.foreach_midi_time_axis (
964 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
966 if (_note_mode != mode || midi_track()->note_mode() != mode) {
968 midi_track()->set_note_mode(mode);
969 set_gui_property ("note-mode", enum_2_string(_note_mode));
970 _view->redisplay_track();
976 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
978 if (apply_to_selection) {
979 _editor.get_selection().tracks.foreach_midi_time_axis (
980 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
982 if (_color_mode == mode && !force) {
986 if (mode == ChannelColors) {
987 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
989 _channel_selector.set_default_channel_color();
993 set_gui_property ("color-mode", enum_2_string(_color_mode));
995 _view->redisplay_track();
1001 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1003 if (apply_to_selection) {
1004 _editor.get_selection().tracks.foreach_midi_time_axis (
1005 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1007 if (!_ignore_signals) {
1008 midi_view()->set_note_range(range);
1014 MidiTimeAxisView::update_range()
1016 MidiGhostRegion* mgr;
1018 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1019 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1020 mgr->update_range();
1026 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1028 if (apply_to_selection) {
1029 _editor.get_selection().tracks.foreach_midi_time_axis (
1030 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1033 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1035 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1036 create_automation_child(*i, true);
1040 RouteTimeAxisView::show_all_automation ();
1045 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1047 if (apply_to_selection) {
1048 _editor.get_selection().tracks.foreach_midi_time_axis (
1049 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1052 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1054 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1055 create_automation_child (*i, true);
1059 RouteTimeAxisView::show_existing_automation ();
1063 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1066 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1068 if (param.type() == NullAutomation) {
1072 AutomationTracks::iterator existing = _automation_tracks.find (param);
1074 if (existing != _automation_tracks.end()) {
1076 /* automation track created because we had existing data for
1077 * the processor, but visibility may need to be controlled
1078 * since it will have been set visible by default.
1081 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1088 boost::shared_ptr<AutomationTimeAxisView> track;
1090 switch (param.type()) {
1092 case GainAutomation:
1093 create_gain_automation_child (param, show);
1096 case PluginAutomation:
1097 /* handled elsewhere */
1100 case MidiCCAutomation:
1101 case MidiPgmChangeAutomation:
1102 case MidiPitchBenderAutomation:
1103 case MidiChannelPressureAutomation:
1104 case MidiSystemExclusiveAutomation:
1105 /* These controllers are region "automation" - they are owned
1106 * by regions (and their MidiModels), not by the track. As a
1107 * result we do not create an AutomationList/Line for the track
1108 * ... except here we are doing something!! XXX
1111 track.reset (new AutomationTimeAxisView (
1114 boost::shared_ptr<Automatable> (),
1115 boost::shared_ptr<AutomationControl> (),
1121 _route->describe_parameter(param)));
1124 _view->foreach_regionview (
1125 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1128 add_automation_child (param, track, show);
1132 error << "MidiTimeAxisView: unknown automation child "
1133 << EventTypeMap::instance().to_symbol(param) << endmsg;
1138 MidiTimeAxisView::route_active_changed ()
1140 RouteUI::route_active_changed ();
1143 if (_route->active()) {
1144 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1145 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1146 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1148 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1149 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1150 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1153 if (_route->active()) {
1154 controls_ebox.set_name ("BusControlsBaseUnselected");
1155 controls_base_selected_name = "BusControlsBaseSelected";
1156 controls_base_unselected_name = "BusControlsBaseUnselected";
1158 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1159 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1160 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1166 MidiTimeAxisView::set_note_selection (uint8_t note)
1168 if (!_editor.internal_editing()) {
1172 uint16_t chn_mask = _channel_selector.get_selected_channels();
1174 if (_view->num_selected_regionviews() == 0) {
1175 _view->foreach_regionview (
1176 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1179 _view->foreach_selected_regionview (
1180 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1186 MidiTimeAxisView::add_note_selection (uint8_t note)
1188 if (!_editor.internal_editing()) {
1192 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1194 if (_view->num_selected_regionviews() == 0) {
1195 _view->foreach_regionview (
1196 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1199 _view->foreach_selected_regionview (
1200 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1206 MidiTimeAxisView::extend_note_selection (uint8_t note)
1208 if (!_editor.internal_editing()) {
1212 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1214 if (_view->num_selected_regionviews() == 0) {
1215 _view->foreach_regionview (
1216 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1219 _view->foreach_selected_regionview (
1220 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1226 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1228 if (!_editor.internal_editing()) {
1232 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1234 if (_view->num_selected_regionviews() == 0) {
1235 _view->foreach_regionview (
1236 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1239 _view->foreach_selected_regionview (
1240 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1246 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1248 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1252 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1254 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1258 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1260 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1264 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1266 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1270 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1272 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1276 const uint16_t selected_channels = _channel_selector.get_selected_channels();
1277 bool changed = false;
1281 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1283 for (uint32_t chn = 0; chn < 16; ++chn) {
1284 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1285 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1291 if ((selected_channels & (0x0001 << chn)) == 0) {
1292 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1293 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1295 changed = track->set_marked_for_display (false) || changed;
1297 changed = track->set_marked_for_display (true) || changed;
1304 /* TODO: Bender, Pressure */
1306 /* invalidate the controller menu, so that we rebuild it next time */
1307 _controller_menu_map.clear ();
1308 delete controller_menu;
1309 controller_menu = 0;
1317 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1319 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1324 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1325 if (i != _controller_menu_map.end()) {
1329 i = _channel_command_menu_map.find (param);
1330 if (i != _channel_command_menu_map.end()) {
1337 boost::shared_ptr<MidiRegion>
1338 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1340 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1342 real_editor->begin_reversible_command (Operations::create_region);
1343 playlist()->clear_changes ();
1345 real_editor->snap_to (pos, 0);
1347 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1348 view()->trackview().track().get(), view()->trackview().track()->name());
1351 plist.add (ARDOUR::Properties::start, 0);
1352 plist.add (ARDOUR::Properties::length, length);
1353 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1355 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1357 playlist()->add_region (region, pos);
1358 _session->add_command (new StatefulDiffCommand (playlist()));
1361 real_editor->commit_reversible_command ();
1364 return boost::dynamic_pointer_cast<MidiRegion>(region);
1368 MidiTimeAxisView::ensure_step_editor ()
1370 if (!_step_editor) {
1371 _step_editor = new StepEditor (_editor, midi_track(), *this);
1376 MidiTimeAxisView::start_step_editing ()
1378 ensure_step_editor ();
1379 _step_editor->start_step_editing ();
1383 MidiTimeAxisView::stop_step_editing ()
1386 _step_editor->stop_step_editing ();
1391 /** @return channel (counted from 0) to add an event to, based on the current setting
1392 * of the channel selector.
1395 MidiTimeAxisView::get_channel_for_add () const
1397 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1399 uint8_t channel = 0;
1401 /* pick the highest selected channel, unless all channels are selected,
1402 which is interpreted to mean channel 1 (zero)
1405 for (uint16_t i = 0; i < 16; ++i) {
1406 if (chn_mask & (1<<i)) {
1412 if (chn_cnt == 16) {
1420 MidiTimeAxisView::note_range_changed ()
1422 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1423 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1427 MidiTimeAxisView::contents_height_changed ()
1429 _range_scroomer->set_size_request (-1, _view->child_height ());