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_scroomer.h"
70 #include "midi_streamview.h"
71 #include "midi_region_view.h"
72 #include "midi_time_axis.h"
73 #include "piano_roll_header.h"
74 #include "playlist_selector.h"
75 #include "plugin_selector.h"
76 #include "plugin_ui.h"
77 #include "point_selection.h"
79 #include "region_view.h"
80 #include "rgb_macros.h"
81 #include "selection.h"
82 #include "step_editor.h"
83 #include "simplerect.h"
86 #include "ardour/midi_track.h"
90 using namespace ARDOUR;
93 using namespace Gtkmm2ext;
94 using namespace Editing;
96 // Minimum height at which a control is displayed
97 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
98 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
100 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
101 : AxisView(sess) // virtually inherited
102 , RouteTimeAxisView(ed, sess, canvas)
103 , _ignore_signals(false)
105 , _piano_roll_header(0)
106 , _note_mode(Sustained)
108 , _percussion_mode_item(0)
109 , _color_mode(MeterColors)
110 , _meter_color_mode_item(0)
111 , _channel_color_mode_item(0)
112 , _track_color_mode_item(0)
113 , _step_edit_item (0)
114 , controller_menu (0)
120 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
124 _view = new MidiStreamView (*this);
127 _piano_roll_header = new PianoRollHeader(*midi_view());
128 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
129 _range_scroomer->DoubleClicked.connect (
130 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
131 MidiStreamView::ContentsRange, false));
134 /* This next call will result in our height being set up, so it must come after
135 the creation of the piano roll / range scroomer as their visibility is set up
138 RouteTimeAxisView::set_route (rt);
140 _view->apply_color (_color, StreamView::RegionColor);
142 subplugin_menu.set_name ("ArdourContextMenu");
144 if (!gui_property ("note-range-min").empty ()) {
145 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
146 atoi (gui_property ("note-range-max").c_str()),
150 midi_view()->NoteRangeChanged.connect (
151 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
152 _view->ContentsHeightChanged.connect (
153 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
155 ignore_toggle = false;
157 if (is_midi_track()) {
158 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
159 _note_mode = midi_track()->note_mode();
160 } else { // MIDI bus (which doesn't exist yet..)
161 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
164 /* map current state of the route */
166 processors_changed (RouteProcessorChange ());
168 _route->processors_changed.connect (*this, invalidator (*this),
169 boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
173 _piano_roll_header->SetNoteSelection.connect (
174 sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
175 _piano_roll_header->AddNoteSelection.connect (
176 sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
177 _piano_roll_header->ExtendNoteSelection.connect (
178 sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
179 _piano_roll_header->ToggleNoteSelection.connect (
180 sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
182 /* Suspend updates of the StreamView during scroomer drags to speed things up */
183 _range_scroomer->DragStarting.connect (
184 sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
185 _range_scroomer->DragFinishing.connect (
186 sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
188 /* Put the scroomer and the keyboard in a VBox with a padding
189 label so that they can be reduced in height for stacked-view
192 VBox* v = manage (new VBox);
193 HBox* h = manage (new HBox);
194 h->pack_start (*_range_scroomer);
195 h->pack_start (*_piano_roll_header);
196 v->pack_start (*h, false, false);
197 v->pack_start (*manage (new Label ("")), true, true);
200 controls_hbox.pack_start(*v);
202 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
203 controls_base_selected_name = "MidiTrackControlsBaseSelected";
204 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
206 midi_view()->NoteRangeChanged.connect (
207 sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
209 /* ask for notifications of any new RegionViews */
210 _view->RegionViewAdded.connect (
211 sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
213 if (!_editor.have_idled()) {
214 /* first idle will do what we need */
220 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
222 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
223 for (; m != patch_manager.all_models().end(); ++m) {
224 _midnam_model_selector.append_text(m->c_str());
227 if (gui_property (X_("midnam-model-name")).empty()) {
228 set_gui_property (X_("midnam-model-name"), "Generic");
231 if (gui_property (X_("midnam-custom-device-mode")).empty()) {
232 boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
234 set_gui_property (X_("midnam-custom-device-mode"),
235 *device_names->custom_device_mode_names().begin());
239 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
240 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
242 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
243 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
245 _midi_controls_box.set_homogeneous(false);
246 _midi_controls_box.set_border_width (10);
248 if (!patch_manager.all_models().empty()) {
249 _channel_selector.set_border_width(2);
250 _channel_selector.show_all ();
252 _midi_controls_box.resize(3, 2);
253 _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1);
255 _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2);
257 _midnam_model_selector.set_size_request(22, 30);
258 _midnam_model_selector.set_border_width(2);
259 _midnam_model_selector.show ();
260 _midi_controls_box.attach(_midnam_model_selector, 0, 1, 2, 3);
262 _midnam_custom_device_mode_selector.set_size_request(10, 30);
263 _midnam_custom_device_mode_selector.set_border_width(2);
264 _midnam_custom_device_mode_selector.show ();
266 _midi_controls_box.attach(_midnam_custom_device_mode_selector, 0, 1, 3, 4);
268 _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1);
269 _channel_selector.show_all ();
273 custom_device_mode_changed();
275 _midnam_model_selector.signal_changed().connect(
276 sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
277 _midnam_custom_device_mode_selector.signal_changed().connect(
278 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
280 controls_vbox.pack_start(_midi_controls_box, false, false);
282 // restore channel selector settings
283 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(),
284 midi_track()->get_channel_mask());
285 _channel_selector.mode_changed.connect(
286 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
287 _channel_selector.mode_changed.connect(
288 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
290 const string color_mode = gui_property ("color-mode");
291 if (!color_mode.empty()) {
292 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
293 if (_color_mode == ChannelColors) {
294 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
298 set_color_mode (_color_mode, true, false);
300 const string note_mode = gui_property ("note-mode");
301 if (!note_mode.empty()) {
302 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
303 if (_percussion_mode_item) {
304 _percussion_mode_item->set_active (_note_mode == Percussive);
308 /* Look for any GUI object state nodes that represent automation children
309 * that should exist, and create the children.
312 const list<string> gui_ids = gui_object_state().all_ids ();
313 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
316 Evoral::Parameter parameter (0, 0, 0);
318 bool const p = AutomationTimeAxisView::parse_state_id (
319 *i, route_id, has_parameter, parameter);
320 if (p && route_id == _route->id () && has_parameter) {
321 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
322 create_automation_child (parameter, string_is_affirmative (visible));
328 MidiTimeAxisView::first_idle ()
335 MidiTimeAxisView::~MidiTimeAxisView ()
337 delete _piano_roll_header;
338 _piano_roll_header = 0;
340 delete _range_scroomer;
343 delete controller_menu;
348 MidiTimeAxisView::enter_internal_edit_mode ()
351 midi_view()->enter_internal_edit_mode ();
356 MidiTimeAxisView::leave_internal_edit_mode ()
359 midi_view()->leave_internal_edit_mode ();
364 MidiTimeAxisView::check_step_edit ()
366 ensure_step_editor ();
367 _step_editor->check_step_edit ();
371 MidiTimeAxisView::model_changed()
373 const Glib::ustring model = _midnam_model_selector.get_active_text();
374 set_gui_property (X_("midnam-model-name"), model);
376 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
377 .custom_device_mode_names_by_model(model);
379 _midnam_custom_device_mode_selector.clear_items();
381 for (std::list<std::string>::const_iterator i = device_modes.begin();
382 i != device_modes.end(); ++i) {
383 _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);
759 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
760 MidiTimeAxisView::get_device_mode()
762 using namespace MIDI::Name;
764 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
766 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
769 return device_names->custom_device_mode_by_name(
770 gui_property (X_("midnam-custom-device-mode")));
773 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
774 MidiTimeAxisView::get_device_names()
776 using namespace MIDI::Name;
778 const std::string model = gui_property (X_("midnam-model-name"));
780 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
781 .document_by_model(model);
783 return midnam->master_device_names(model);
785 return boost::shared_ptr<MasterDeviceNames>();
790 MidiTimeAxisView::build_controller_menu ()
792 using namespace Menu_Helpers;
794 if (controller_menu) {
795 /* it exists and has not been invalidated by a channel mode change */
799 controller_menu = new Menu; // explicitly managed by us
800 MenuList& items (controller_menu->items());
802 /* create several "top level" menu items for sets of controllers (16 at a
803 time), and populate each one with a submenu for each controller+channel
804 combination covering the currently selected channels for this track
807 const uint16_t selected_channels = _channel_selector.get_selected_channels();
809 /* count the number of selected channels because we will build a different menu
810 structure if there is more than 1 selected.
814 for (uint8_t chn = 0; chn < 16; chn++) {
815 if (selected_channels & (0x0001 << chn)) {
822 using namespace MIDI::Name;
823 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
825 if (device_names && !device_names->controls().empty()) {
826 /* Controllers names available in midnam file, generate fancy menu */
827 unsigned n_items = 0;
828 unsigned n_groups = 0;
830 /* TODO: This is not correct, should look up the currently applicable ControlNameList
831 and only build a menu for that one. */
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->second;
835 Menu* ctl_menu = NULL;
837 for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
838 c != name_list->controls().end();) {
839 const uint16_t ctl = c->second->number();
840 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
841 /* Skip bank select controllers since they're handled specially */
843 /* Create a new submenu */
844 ctl_menu = manage (new Menu);
847 MenuList& ctl_items (ctl_menu->items());
849 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
851 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
856 if (ctl_menu && (++n_items == 16 || c == name_list->controls().end())) {
857 /* Submenu has 16 items or we're done, add it to controller menu and reset */
859 MenuElem(string_compose(_("Controllers %1-%2"),
860 (16 * n_groups), (16 * n_groups) + n_items - 1),
869 /* No controllers names, generate generic numeric menu */
870 for (int i = 0; i < 127; i += 16) {
871 Menu* ctl_menu = manage (new Menu);
872 MenuList& ctl_items (ctl_menu->items());
874 for (int ctl = i; ctl < i+16; ++ctl) {
875 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
876 /* Skip bank select controllers since they're handled specially */
881 add_multi_channel_controller_item(
882 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
884 add_single_channel_controller_item(
885 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
889 /* Add submenu for this block of controllers to controller menu */
891 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
898 MidiTimeAxisView::build_note_mode_menu()
900 using namespace Menu_Helpers;
902 Menu* mode_menu = manage (new Menu);
903 MenuList& items = mode_menu->items();
904 mode_menu->set_name ("ArdourContextMenu");
906 RadioMenuItem::Group mode_group;
908 RadioMenuElem (mode_group,_("Sustained"),
909 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
911 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
912 _note_mode_item->set_active(_note_mode == Sustained);
915 RadioMenuElem (mode_group, _("Percussive"),
916 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
918 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
919 _percussion_mode_item->set_active(_note_mode == Percussive);
925 MidiTimeAxisView::build_color_mode_menu()
927 using namespace Menu_Helpers;
929 Menu* mode_menu = manage (new Menu);
930 MenuList& items = mode_menu->items();
931 mode_menu->set_name ("ArdourContextMenu");
933 RadioMenuItem::Group mode_group;
935 RadioMenuElem (mode_group, _("Meter Colors"),
936 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
937 MeterColors, false, true, true)));
938 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
939 _meter_color_mode_item->set_active(_color_mode == MeterColors);
942 RadioMenuElem (mode_group, _("Channel Colors"),
943 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
944 ChannelColors, false, true, true)));
945 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
946 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
949 RadioMenuElem (mode_group, _("Track Color"),
950 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
951 TrackColor, false, true, true)));
952 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
953 _channel_color_mode_item->set_active(_color_mode == TrackColor);
959 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
961 if (apply_to_selection) {
962 _editor.get_selection().tracks.foreach_midi_time_axis (
963 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
965 if (_note_mode != mode || midi_track()->note_mode() != mode) {
967 midi_track()->set_note_mode(mode);
968 set_gui_property ("note-mode", enum_2_string(_note_mode));
969 _view->redisplay_track();
975 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
977 if (apply_to_selection) {
978 _editor.get_selection().tracks.foreach_midi_time_axis (
979 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
981 if (_color_mode == mode && !force) {
985 if (mode == ChannelColors) {
986 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
988 _channel_selector.set_default_channel_color();
992 set_gui_property ("color-mode", enum_2_string(_color_mode));
994 _view->redisplay_track();
1000 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1002 if (apply_to_selection) {
1003 _editor.get_selection().tracks.foreach_midi_time_axis (
1004 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1006 if (!_ignore_signals) {
1007 midi_view()->set_note_range(range);
1013 MidiTimeAxisView::update_range()
1015 MidiGhostRegion* mgr;
1017 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1018 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1019 mgr->update_range();
1025 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1027 if (apply_to_selection) {
1028 _editor.get_selection().tracks.foreach_midi_time_axis (
1029 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1032 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1034 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1035 create_automation_child(*i, true);
1039 RouteTimeAxisView::show_all_automation ();
1044 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1046 if (apply_to_selection) {
1047 _editor.get_selection().tracks.foreach_midi_time_axis (
1048 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1051 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1053 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1054 create_automation_child (*i, true);
1058 RouteTimeAxisView::show_existing_automation ();
1062 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1065 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1067 if (param.type() == NullAutomation) {
1071 AutomationTracks::iterator existing = _automation_tracks.find (param);
1073 if (existing != _automation_tracks.end()) {
1075 /* automation track created because we had existing data for
1076 * the processor, but visibility may need to be controlled
1077 * since it will have been set visible by default.
1080 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1087 boost::shared_ptr<AutomationTimeAxisView> track;
1089 switch (param.type()) {
1091 case GainAutomation:
1092 create_gain_automation_child (param, show);
1095 case PluginAutomation:
1096 /* handled elsewhere */
1099 case MidiCCAutomation:
1100 case MidiPgmChangeAutomation:
1101 case MidiPitchBenderAutomation:
1102 case MidiChannelPressureAutomation:
1103 case MidiSystemExclusiveAutomation:
1104 /* These controllers are region "automation" - they are owned
1105 * by regions (and their MidiModels), not by the track. As a
1106 * result we do not create an AutomationList/Line for the track
1107 * ... except here we are doing something!! XXX
1110 track.reset (new AutomationTimeAxisView (
1113 boost::shared_ptr<Automatable> (),
1114 boost::shared_ptr<AutomationControl> (),
1120 _route->describe_parameter(param)));
1123 _view->foreach_regionview (
1124 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1127 add_automation_child (param, track, show);
1131 error << "MidiTimeAxisView: unknown automation child "
1132 << EventTypeMap::instance().to_symbol(param) << endmsg;
1137 MidiTimeAxisView::route_active_changed ()
1139 RouteUI::route_active_changed ();
1142 if (_route->active()) {
1143 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1144 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1145 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1147 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1148 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1149 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1152 if (_route->active()) {
1153 controls_ebox.set_name ("BusControlsBaseUnselected");
1154 controls_base_selected_name = "BusControlsBaseSelected";
1155 controls_base_unselected_name = "BusControlsBaseUnselected";
1157 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1158 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1159 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1165 MidiTimeAxisView::set_note_selection (uint8_t note)
1167 if (!_editor.internal_editing()) {
1171 uint16_t chn_mask = _channel_selector.get_selected_channels();
1173 if (_view->num_selected_regionviews() == 0) {
1174 _view->foreach_regionview (
1175 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1178 _view->foreach_selected_regionview (
1179 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1185 MidiTimeAxisView::add_note_selection (uint8_t note)
1187 if (!_editor.internal_editing()) {
1191 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1193 if (_view->num_selected_regionviews() == 0) {
1194 _view->foreach_regionview (
1195 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1198 _view->foreach_selected_regionview (
1199 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1205 MidiTimeAxisView::extend_note_selection (uint8_t note)
1207 if (!_editor.internal_editing()) {
1211 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1213 if (_view->num_selected_regionviews() == 0) {
1214 _view->foreach_regionview (
1215 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1218 _view->foreach_selected_regionview (
1219 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1225 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1227 if (!_editor.internal_editing()) {
1231 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1233 if (_view->num_selected_regionviews() == 0) {
1234 _view->foreach_regionview (
1235 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1238 _view->foreach_selected_regionview (
1239 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1245 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1247 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1251 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1253 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1257 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1259 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1263 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1265 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1269 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1271 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1275 const uint16_t selected_channels = _channel_selector.get_selected_channels();
1276 bool changed = false;
1280 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1282 for (uint32_t chn = 0; chn < 16; ++chn) {
1283 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1284 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1290 if ((selected_channels & (0x0001 << chn)) == 0) {
1291 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1292 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1294 changed = track->set_marked_for_display (false) || changed;
1296 changed = track->set_marked_for_display (true) || changed;
1303 /* TODO: Bender, Pressure */
1305 /* invalidate the controller menu, so that we rebuild it next time */
1306 _controller_menu_map.clear ();
1307 delete controller_menu;
1308 controller_menu = 0;
1316 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1318 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1323 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1324 if (i != _controller_menu_map.end()) {
1328 i = _channel_command_menu_map.find (param);
1329 if (i != _channel_command_menu_map.end()) {
1336 boost::shared_ptr<MidiRegion>
1337 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1339 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1341 real_editor->begin_reversible_command (Operations::create_region);
1342 playlist()->clear_changes ();
1344 real_editor->snap_to (pos, 0);
1346 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1347 view()->trackview().track().get(), view()->trackview().track()->name());
1350 plist.add (ARDOUR::Properties::start, 0);
1351 plist.add (ARDOUR::Properties::length, length);
1352 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1354 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1356 playlist()->add_region (region, pos);
1357 _session->add_command (new StatefulDiffCommand (playlist()));
1360 real_editor->commit_reversible_command ();
1363 return boost::dynamic_pointer_cast<MidiRegion>(region);
1367 MidiTimeAxisView::ensure_step_editor ()
1369 if (!_step_editor) {
1370 _step_editor = new StepEditor (_editor, midi_track(), *this);
1375 MidiTimeAxisView::start_step_editing ()
1377 ensure_step_editor ();
1378 _step_editor->start_step_editing ();
1382 MidiTimeAxisView::stop_step_editing ()
1385 _step_editor->stop_step_editing ();
1389 /** @return channel (counted from 0) to add an event to, based on the current setting
1390 * of the channel selector.
1393 MidiTimeAxisView::get_channel_for_add () const
1395 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1397 uint8_t channel = 0;
1399 /* pick the highest selected channel, unless all channels are selected,
1400 which is interpreted to mean channel 1 (zero)
1403 for (uint16_t i = 0; i < 16; ++i) {
1404 if (chn_mask & (1<<i)) {
1410 if (chn_cnt == 16) {
1418 MidiTimeAxisView::note_range_changed ()
1420 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1421 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1425 MidiTimeAxisView::contents_height_changed ()
1427 _range_scroomer->set_size_request (-1, _view->child_height ());