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 "ardour_ui.h"
59 #include "ardour_button.h"
60 #include "automation_line.h"
61 #include "automation_time_axis.h"
62 #include "canvas-note-event.h"
63 #include "canvas_impl.h"
66 #include "ghostregion.h"
67 #include "gui_thread.h"
69 #include "midi_channel_selector.h"
70 #include "midi_scroomer.h"
71 #include "midi_streamview.h"
72 #include "midi_region_view.h"
73 #include "midi_time_axis.h"
74 #include "piano_roll_header.h"
75 #include "playlist_selector.h"
76 #include "plugin_selector.h"
77 #include "plugin_ui.h"
78 #include "point_selection.h"
80 #include "region_view.h"
81 #include "rgb_macros.h"
82 #include "selection.h"
83 #include "step_editor.h"
84 #include "simplerect.h"
87 #include "ardour/midi_track.h"
91 using namespace ARDOUR;
94 using namespace Gtkmm2ext;
95 using namespace Editing;
97 // Minimum height at which a control is displayed
98 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 130;
99 static const uint32_t KEYBOARD_MIN_HEIGHT = 130;
101 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
102 : AxisView(sess) // virtually inherited
103 , RouteTimeAxisView(ed, sess, canvas)
104 , _ignore_signals(false)
106 , _piano_roll_header(0)
107 , _note_mode(Sustained)
109 , _percussion_mode_item(0)
110 , _color_mode(MeterColors)
111 , _meter_color_mode_item(0)
112 , _channel_color_mode_item(0)
113 , _track_color_mode_item(0)
114 , _channel_selector (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 */
222 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
224 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
225 for (; m != patch_manager.all_models().end(); ++m) {
226 _midnam_model_selector.append_text(m->c_str());
229 if (gui_property (X_("midnam-model-name")).empty()) {
230 set_gui_property (X_("midnam-model-name"), "Generic");
233 if (gui_property (X_("midnam-custom-device-mode")).empty()) {
234 boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
236 set_gui_property (X_("midnam-custom-device-mode"),
237 *device_names->custom_device_mode_names().begin());
241 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
242 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
244 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
245 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
247 _midi_controls_box.set_homogeneous(false);
248 _midi_controls_box.set_border_width (10);
250 if (!patch_manager.all_models().empty()) {
251 _midi_controls_box.resize(2, 2);
253 _midnam_model_selector.set_size_request(22, 30);
254 _midnam_model_selector.set_border_width(2);
255 _midnam_model_selector.show ();
256 _midi_controls_box.attach(_midnam_model_selector, 0, 1, 0, 1);
258 _midnam_custom_device_mode_selector.set_size_request(10, 30);
259 _midnam_custom_device_mode_selector.set_border_width(2);
260 _midnam_custom_device_mode_selector.show ();
262 _midi_controls_box.attach(_midnam_custom_device_mode_selector, 0, 1, 1, 2);
266 custom_device_mode_changed();
268 _midnam_model_selector.signal_changed().connect(
269 sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
270 _midnam_custom_device_mode_selector.signal_changed().connect(
271 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
273 controls_vbox.pack_start(_midi_controls_box, false, false);
275 const string color_mode = gui_property ("color-mode");
276 if (!color_mode.empty()) {
277 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
278 if (_channel_selector && _color_mode == ChannelColors) {
279 _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
283 set_color_mode (_color_mode, true, false);
285 const string note_mode = gui_property ("note-mode");
286 if (!note_mode.empty()) {
287 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
288 if (_percussion_mode_item) {
289 _percussion_mode_item->set_active (_note_mode == Percussive);
293 /* Look for any GUI object state nodes that represent automation children
294 * that should exist, and create the children.
297 const list<string> gui_ids = gui_object_state().all_ids ();
298 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
301 Evoral::Parameter parameter (0, 0, 0);
303 bool const p = AutomationTimeAxisView::parse_state_id (
304 *i, route_id, has_parameter, parameter);
305 if (p && route_id == _route->id () && has_parameter) {
306 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
307 create_automation_child (parameter, string_is_affirmative (visible));
313 MidiTimeAxisView::first_idle ()
320 MidiTimeAxisView::~MidiTimeAxisView ()
322 delete _channel_selector;
324 delete _piano_roll_header;
325 _piano_roll_header = 0;
327 delete _range_scroomer;
330 delete controller_menu;
335 MidiTimeAxisView::enter_internal_edit_mode ()
338 midi_view()->enter_internal_edit_mode ();
343 MidiTimeAxisView::leave_internal_edit_mode ()
346 midi_view()->leave_internal_edit_mode ();
351 MidiTimeAxisView::check_step_edit ()
353 ensure_step_editor ();
354 _step_editor->check_step_edit ();
358 MidiTimeAxisView::model_changed()
360 const Glib::ustring model = _midnam_model_selector.get_active_text();
361 set_gui_property (X_("midnam-model-name"), model);
363 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
364 .custom_device_mode_names_by_model(model);
366 _midnam_custom_device_mode_selector.clear_items();
368 for (std::list<std::string>::const_iterator i = device_modes.begin();
369 i != device_modes.end(); ++i) {
370 _midnam_custom_device_mode_selector.append_text(*i);
373 _midnam_custom_device_mode_selector.set_active(0);
375 _route->instrument_info().set_external_instrument (
376 _midnam_model_selector.get_active_text(),
377 _midnam_custom_device_mode_selector.get_active_text());
379 // Rebuild controller menu
380 _controller_menu_map.clear ();
381 delete controller_menu;
383 build_automation_action_menu(false);
387 MidiTimeAxisView::custom_device_mode_changed()
389 const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text();
390 set_gui_property (X_("midnam-custom-device-mode"), mode);
391 _route->instrument_info().set_external_instrument (
392 _midnam_model_selector.get_active_text(), mode);
396 MidiTimeAxisView::midi_view()
398 return dynamic_cast<MidiStreamView*>(_view);
402 MidiTimeAxisView::set_height (uint32_t h)
404 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
405 _midi_controls_box.show ();
407 _midi_controls_box.hide();
410 if (h >= KEYBOARD_MIN_HEIGHT) {
411 if (is_track() && _range_scroomer) {
412 _range_scroomer->show();
414 if (is_track() && _piano_roll_header) {
415 _piano_roll_header->show();
418 if (is_track() && _range_scroomer) {
419 _range_scroomer->hide();
421 if (is_track() && _piano_roll_header) {
422 _piano_roll_header->hide();
426 /* We need to do this after changing visibility of our stuff, as it will
427 eventually trigger a call to Editor::reset_controls_layout_width(),
428 which needs to know if we have just shown or hidden a scroomer /
431 RouteTimeAxisView::set_height (h);
435 MidiTimeAxisView::append_extra_display_menu_items ()
437 using namespace Menu_Helpers;
439 MenuList& items = display_menu->items();
442 Menu *range_menu = manage(new Menu);
443 MenuList& range_items = range_menu->items();
444 range_menu->set_name ("ArdourContextMenu");
446 range_items.push_back (
447 MenuElem (_("Show Full Range"),
448 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
449 MidiStreamView::FullRange, true)));
451 range_items.push_back (
452 MenuElem (_("Fit Contents"),
453 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
454 MidiStreamView::ContentsRange, true)));
456 items.push_back (MenuElem (_("Note Range"), *range_menu));
457 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
458 items.push_back (MenuElem (_("Channel Selector"),
459 sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector)));
461 color_mode_menu = build_color_mode_menu();
462 if (color_mode_menu) {
463 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
466 items.push_back (SeparatorElem ());
470 MidiTimeAxisView::toggle_channel_selector ()
472 if (!_channel_selector) {
473 _channel_selector = new MidiChannelSelectorWindow (midi_track());
475 if (_color_mode == ChannelColors) {
476 _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
478 _channel_selector->set_default_channel_color ();
481 _channel_selector->show_all ();
483 _channel_selector->cycle_visibility ();
488 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
490 using namespace Menu_Helpers;
492 /* If we have a controller menu, we need to detach it before
493 RouteTimeAxis::build_automation_action_menu destroys the
494 menu it is attached to. Otherwise GTK destroys
495 controller_menu's gobj, meaning that it can't be reattached
496 below. See bug #3134.
499 if (controller_menu) {
500 detach_menu (*controller_menu);
503 _channel_command_menu_map.clear ();
504 RouteTimeAxisView::build_automation_action_menu (for_selection);
506 MenuList& automation_items = automation_action_menu->items();
508 uint16_t selected_channels = midi_track()->get_playback_channel_mask();
510 if (selected_channels != 0) {
512 automation_items.push_back (SeparatorElem());
514 /* these 2 MIDI "command" types are semantically more like automation
515 than note data, but they are not MIDI controllers. We give them
516 special status in this menu, since they will not show up in the
517 controller list and anyone who actually knows something about MIDI
518 (!) would not expect to find them there.
521 add_channel_command_menu_item (
522 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
523 automation_items.back().set_sensitive (
524 !for_selection || _editor.get_selection().tracks.size() == 1);
525 add_channel_command_menu_item (
526 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
527 automation_items.back().set_sensitive (
528 !for_selection || _editor.get_selection().tracks.size() == 1);
530 /* now all MIDI controllers. Always offer the possibility that we will
531 rebuild the controllers menu since it might need to be updated after
532 a channel mode change or other change. Also detach it first in case
533 it has been used anywhere else.
536 build_controller_menu ();
538 automation_items.push_back (SeparatorElem());
539 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
540 automation_items.back().set_sensitive (
541 !for_selection || _editor.get_selection().tracks.size() == 1);
543 automation_items.push_back (
544 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
545 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
550 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
552 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
554 for (uint8_t chn = 0; chn < 16; chn++) {
555 if (selected_channels & (0x0001 << chn)) {
557 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
558 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
561 menu->set_active (yn);
568 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
570 AutomationType auto_type,
573 using namespace Menu_Helpers;
575 /* count the number of selected channels because we will build a different menu
576 structure if there is more than 1 selected.
579 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
582 for (uint8_t chn = 0; chn < 16; chn++) {
583 if (selected_channels & (0x0001 << chn)) {
592 /* multiple channels - create a submenu, with 1 item per channel */
594 Menu* chn_menu = manage (new Menu);
595 MenuList& chn_items (chn_menu->items());
596 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
598 /* add a couple of items to hide/show all of them */
600 chn_items.push_back (
601 MenuElem (_("Hide all channels"),
602 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
603 false, param_without_channel)));
604 chn_items.push_back (
605 MenuElem (_("Show all channels"),
606 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
607 true, param_without_channel)));
609 for (uint8_t chn = 0; chn < 16; chn++) {
610 if (selected_channels & (0x0001 << chn)) {
612 /* for each selected channel, add a menu item for this controller */
614 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
615 chn_items.push_back (
616 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
617 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
618 fully_qualified_param)));
620 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
621 bool visible = false;
624 if (track->marked_for_display()) {
629 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
630 _channel_command_menu_map[fully_qualified_param] = cmi;
631 cmi->set_active (visible);
635 /* now create an item in the parent menu that has the per-channel list as a submenu */
637 items.push_back (MenuElem (label, *chn_menu));
641 /* just one channel - create a single menu item for this command+channel combination*/
643 for (uint8_t chn = 0; chn < 16; chn++) {
644 if (selected_channels & (0x0001 << chn)) {
646 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
648 CheckMenuElem (label,
649 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
650 fully_qualified_param)));
652 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
653 bool visible = false;
656 if (track->marked_for_display()) {
661 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
662 _channel_command_menu_map[fully_qualified_param] = cmi;
663 cmi->set_active (visible);
665 /* one channel only */
672 /** Add a single menu item for a controller on one channel. */
674 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
676 const std::string& name)
678 using namespace Menu_Helpers;
680 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
681 for (uint8_t chn = 0; chn < 16; chn++) {
682 if (selected_channels & (0x0001 << chn)) {
684 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
685 ctl_items.push_back (
687 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
689 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
690 fully_qualified_param)));
691 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
693 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
694 fully_qualified_param);
696 bool visible = false;
698 if (track->marked_for_display()) {
703 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
704 _controller_menu_map[fully_qualified_param] = cmi;
705 cmi->set_active (visible);
707 /* one channel only */
713 /** Add a submenu with 1 item per channel for a controller on many channels. */
715 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
717 const std::string& name)
719 using namespace Menu_Helpers;
721 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
723 Menu* chn_menu = manage (new Menu);
724 MenuList& chn_items (chn_menu->items());
726 /* add a couple of items to hide/show this controller on all channels */
728 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
729 chn_items.push_back (
730 MenuElem (_("Hide all channels"),
731 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
732 false, param_without_channel)));
733 chn_items.push_back (
734 MenuElem (_("Show all channels"),
735 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
736 true, param_without_channel)));
738 for (uint8_t chn = 0; chn < 16; chn++) {
739 if (selected_channels & (0x0001 << chn)) {
741 /* for each selected channel, add a menu item for this controller */
743 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
744 chn_items.push_back (
745 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
746 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
747 fully_qualified_param)));
749 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
750 fully_qualified_param);
751 bool visible = false;
754 if (track->marked_for_display()) {
759 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
760 _controller_menu_map[fully_qualified_param] = cmi;
761 cmi->set_active (visible);
765 /* add the per-channel menu to the list of controllers, with the name of the controller */
766 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
768 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
771 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
772 MidiTimeAxisView::get_device_mode()
774 using namespace MIDI::Name;
776 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
778 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
781 return device_names->custom_device_mode_by_name(
782 gui_property (X_("midnam-custom-device-mode")));
785 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
786 MidiTimeAxisView::get_device_names()
788 using namespace MIDI::Name;
790 const std::string model = gui_property (X_("midnam-model-name"));
792 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
793 .document_by_model(model);
795 return midnam->master_device_names(model);
797 return boost::shared_ptr<MasterDeviceNames>();
802 MidiTimeAxisView::build_controller_menu ()
804 using namespace Menu_Helpers;
806 if (controller_menu) {
807 /* it exists and has not been invalidated by a channel mode change */
811 controller_menu = new Menu; // explicitly managed by us
812 MenuList& items (controller_menu->items());
814 /* create several "top level" menu items for sets of controllers (16 at a
815 time), and populate each one with a submenu for each controller+channel
816 combination covering the currently selected channels for this track
819 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
821 /* count the number of selected channels because we will build a different menu
822 structure if there is more than 1 selected.
826 for (uint8_t chn = 0; chn < 16; chn++) {
827 if (selected_channels & (0x0001 << chn)) {
834 using namespace MIDI::Name;
835 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
837 if (device_names && !device_names->controls().empty()) {
838 /* Controllers names available in midnam file, generate fancy menu */
839 unsigned n_items = 0;
840 unsigned n_groups = 0;
842 /* TODO: This is not correct, should look up the currently applicable ControlNameList
843 and only build a menu for that one. */
844 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
845 l != device_names->controls().end(); ++l) {
846 boost::shared_ptr<ControlNameList> name_list = l->second;
847 Menu* ctl_menu = NULL;
849 for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
850 c != name_list->controls().end();) {
851 const uint16_t ctl = c->second->number();
852 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
853 /* Skip bank select controllers since they're handled specially */
855 /* Create a new submenu */
856 ctl_menu = manage (new Menu);
859 MenuList& ctl_items (ctl_menu->items());
861 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
863 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
868 if (ctl_menu && (++n_items == 16 || c == name_list->controls().end())) {
869 /* Submenu has 16 items or we're done, add it to controller menu and reset */
871 MenuElem(string_compose(_("Controllers %1-%2"),
872 (16 * n_groups), (16 * n_groups) + n_items - 1),
881 /* No controllers names, generate generic numeric menu */
882 for (int i = 0; i < 127; i += 16) {
883 Menu* ctl_menu = manage (new Menu);
884 MenuList& ctl_items (ctl_menu->items());
886 for (int ctl = i; ctl < i+16; ++ctl) {
887 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
888 /* Skip bank select controllers since they're handled specially */
893 add_multi_channel_controller_item(
894 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
896 add_single_channel_controller_item(
897 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
901 /* Add submenu for this block of controllers to controller menu */
903 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
910 MidiTimeAxisView::build_note_mode_menu()
912 using namespace Menu_Helpers;
914 Menu* mode_menu = manage (new Menu);
915 MenuList& items = mode_menu->items();
916 mode_menu->set_name ("ArdourContextMenu");
918 RadioMenuItem::Group mode_group;
920 RadioMenuElem (mode_group,_("Sustained"),
921 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
923 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
924 _note_mode_item->set_active(_note_mode == Sustained);
927 RadioMenuElem (mode_group, _("Percussive"),
928 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
930 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
931 _percussion_mode_item->set_active(_note_mode == Percussive);
937 MidiTimeAxisView::build_color_mode_menu()
939 using namespace Menu_Helpers;
941 Menu* mode_menu = manage (new Menu);
942 MenuList& items = mode_menu->items();
943 mode_menu->set_name ("ArdourContextMenu");
945 RadioMenuItem::Group mode_group;
947 RadioMenuElem (mode_group, _("Meter Colors"),
948 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
949 MeterColors, false, true, true)));
950 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
951 _meter_color_mode_item->set_active(_color_mode == MeterColors);
954 RadioMenuElem (mode_group, _("Channel Colors"),
955 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
956 ChannelColors, false, true, true)));
957 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
958 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
961 RadioMenuElem (mode_group, _("Track Color"),
962 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
963 TrackColor, false, true, true)));
964 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
965 _channel_color_mode_item->set_active(_color_mode == TrackColor);
971 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
973 if (apply_to_selection) {
974 _editor.get_selection().tracks.foreach_midi_time_axis (
975 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
977 if (_note_mode != mode || midi_track()->note_mode() != mode) {
979 midi_track()->set_note_mode(mode);
980 set_gui_property ("note-mode", enum_2_string(_note_mode));
981 _view->redisplay_track();
987 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
989 if (apply_to_selection) {
990 _editor.get_selection().tracks.foreach_midi_time_axis (
991 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
993 if (_color_mode == mode && !force) {
997 if (_channel_selector) {
998 if (mode == ChannelColors) {
999 _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
1001 _channel_selector->set_default_channel_color();
1006 set_gui_property ("color-mode", enum_2_string(_color_mode));
1008 _view->redisplay_track();
1014 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1016 if (apply_to_selection) {
1017 _editor.get_selection().tracks.foreach_midi_time_axis (
1018 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1020 if (!_ignore_signals) {
1021 midi_view()->set_note_range(range);
1027 MidiTimeAxisView::update_range()
1029 MidiGhostRegion* mgr;
1031 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1032 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1033 mgr->update_range();
1039 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1041 if (apply_to_selection) {
1042 _editor.get_selection().tracks.foreach_midi_time_axis (
1043 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1046 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1048 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1049 create_automation_child(*i, true);
1053 RouteTimeAxisView::show_all_automation ();
1058 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1060 if (apply_to_selection) {
1061 _editor.get_selection().tracks.foreach_midi_time_axis (
1062 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1065 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1067 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1068 create_automation_child (*i, true);
1072 RouteTimeAxisView::show_existing_automation ();
1076 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1079 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1081 if (param.type() == NullAutomation) {
1085 AutomationTracks::iterator existing = _automation_tracks.find (param);
1087 if (existing != _automation_tracks.end()) {
1089 /* automation track created because we had existing data for
1090 * the processor, but visibility may need to be controlled
1091 * since it will have been set visible by default.
1094 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1101 boost::shared_ptr<AutomationTimeAxisView> track;
1103 switch (param.type()) {
1105 case GainAutomation:
1106 create_gain_automation_child (param, show);
1109 case PluginAutomation:
1110 /* handled elsewhere */
1113 case MidiCCAutomation:
1114 case MidiPgmChangeAutomation:
1115 case MidiPitchBenderAutomation:
1116 case MidiChannelPressureAutomation:
1117 case MidiSystemExclusiveAutomation:
1118 /* These controllers are region "automation" - they are owned
1119 * by regions (and their MidiModels), not by the track. As a
1120 * result we do not create an AutomationList/Line for the track
1121 * ... except here we are doing something!! XXX
1124 track.reset (new AutomationTimeAxisView (
1127 boost::shared_ptr<Automatable> (),
1128 boost::shared_ptr<AutomationControl> (),
1134 _route->describe_parameter(param)));
1137 _view->foreach_regionview (
1138 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1141 add_automation_child (param, track, show);
1145 error << "MidiTimeAxisView: unknown automation child "
1146 << EventTypeMap::instance().to_symbol(param) << endmsg;
1151 MidiTimeAxisView::route_active_changed ()
1153 RouteUI::route_active_changed ();
1156 if (_route->active()) {
1157 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1158 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1159 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1161 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1162 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1163 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1166 if (_route->active()) {
1167 controls_ebox.set_name ("BusControlsBaseUnselected");
1168 controls_base_selected_name = "BusControlsBaseSelected";
1169 controls_base_unselected_name = "BusControlsBaseUnselected";
1171 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1172 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1173 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1179 MidiTimeAxisView::set_note_selection (uint8_t note)
1181 if (!_editor.internal_editing()) {
1185 uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1187 if (_view->num_selected_regionviews() == 0) {
1188 _view->foreach_regionview (
1189 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1192 _view->foreach_selected_regionview (
1193 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1199 MidiTimeAxisView::add_note_selection (uint8_t note)
1201 if (!_editor.internal_editing()) {
1205 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1207 if (_view->num_selected_regionviews() == 0) {
1208 _view->foreach_regionview (
1209 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1212 _view->foreach_selected_regionview (
1213 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1219 MidiTimeAxisView::extend_note_selection (uint8_t note)
1221 if (!_editor.internal_editing()) {
1225 const 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::extend_note_selection_region_view),
1232 _view->foreach_selected_regionview (
1233 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1239 MidiTimeAxisView::toggle_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::toggle_note_selection_region_view),
1252 _view->foreach_selected_regionview (
1253 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1259 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1261 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1265 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1267 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1271 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1273 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1277 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1279 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1283 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1285 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1289 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1290 bool changed = false;
1294 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1296 for (uint32_t chn = 0; chn < 16; ++chn) {
1297 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1298 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1304 if ((selected_channels & (0x0001 << chn)) == 0) {
1305 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1306 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1308 changed = track->set_marked_for_display (false) || changed;
1310 changed = track->set_marked_for_display (true) || changed;
1317 /* TODO: Bender, Pressure */
1319 /* invalidate the controller menu, so that we rebuild it next time */
1320 _controller_menu_map.clear ();
1321 delete controller_menu;
1322 controller_menu = 0;
1330 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1332 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1337 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1338 if (i != _controller_menu_map.end()) {
1342 i = _channel_command_menu_map.find (param);
1343 if (i != _channel_command_menu_map.end()) {
1350 boost::shared_ptr<MidiRegion>
1351 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1353 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1355 real_editor->begin_reversible_command (Operations::create_region);
1356 playlist()->clear_changes ();
1358 real_editor->snap_to (pos, 0);
1360 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1361 view()->trackview().track().get(), view()->trackview().track()->name());
1364 plist.add (ARDOUR::Properties::start, 0);
1365 plist.add (ARDOUR::Properties::length, length);
1366 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1368 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1370 playlist()->add_region (region, pos);
1371 _session->add_command (new StatefulDiffCommand (playlist()));
1374 real_editor->commit_reversible_command ();
1377 return boost::dynamic_pointer_cast<MidiRegion>(region);
1381 MidiTimeAxisView::ensure_step_editor ()
1383 if (!_step_editor) {
1384 _step_editor = new StepEditor (_editor, midi_track(), *this);
1389 MidiTimeAxisView::start_step_editing ()
1391 ensure_step_editor ();
1392 _step_editor->start_step_editing ();
1396 MidiTimeAxisView::stop_step_editing ()
1399 _step_editor->stop_step_editing ();
1403 /** @return channel (counted from 0) to add an event to, based on the current setting
1404 * of the channel selector.
1407 MidiTimeAxisView::get_channel_for_add () const
1409 uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
1411 uint8_t channel = 0;
1413 /* pick the highest selected channel, unless all channels are selected,
1414 which is interpreted to mean channel 1 (zero)
1417 for (uint16_t i = 0; i < 16; ++i) {
1418 if (chn_mask & (1<<i)) {
1424 if (chn_cnt == 16) {
1432 MidiTimeAxisView::note_range_changed ()
1434 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1435 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1439 MidiTimeAxisView::contents_height_changed ()
1441 _range_scroomer->set_size_request (-1, _view->child_height ());