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 */
842 /* FIXME: If this is the last control, the last submenu might be lost */
847 /* Create a new submenu */
848 ctl_menu = manage (new Menu);
851 MenuList& ctl_items (ctl_menu->items());
853 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
855 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
859 if (++n_items == 16 || c == name_list->controls().end()) {
860 /* Submenu has 16 items, add it to controller menu and reset */
862 MenuElem(string_compose(_("Controllers %1-%2"),
863 (16 * n_groups), (16 * n_groups) + n_items - 1),
872 /* No controllers names, generate generic numeric menu */
873 for (int i = 0; i < 127; i += 16) {
874 Menu* ctl_menu = manage (new Menu);
875 MenuList& ctl_items (ctl_menu->items());
877 for (int ctl = i; ctl < i+16; ++ctl) {
878 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
879 /* Skip bank select controllers since they're handled specially */
884 add_multi_channel_controller_item(
885 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
887 add_single_channel_controller_item(
888 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
892 /* Add submenu for this block of controllers to controller menu */
894 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
901 MidiTimeAxisView::build_note_mode_menu()
903 using namespace Menu_Helpers;
905 Menu* mode_menu = manage (new Menu);
906 MenuList& items = mode_menu->items();
907 mode_menu->set_name ("ArdourContextMenu");
909 RadioMenuItem::Group mode_group;
911 RadioMenuElem (mode_group,_("Sustained"),
912 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
914 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
915 _note_mode_item->set_active(_note_mode == Sustained);
918 RadioMenuElem (mode_group, _("Percussive"),
919 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
921 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
922 _percussion_mode_item->set_active(_note_mode == Percussive);
928 MidiTimeAxisView::build_color_mode_menu()
930 using namespace Menu_Helpers;
932 Menu* mode_menu = manage (new Menu);
933 MenuList& items = mode_menu->items();
934 mode_menu->set_name ("ArdourContextMenu");
936 RadioMenuItem::Group mode_group;
938 RadioMenuElem (mode_group, _("Meter Colors"),
939 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
940 MeterColors, false, true, true)));
941 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
942 _meter_color_mode_item->set_active(_color_mode == MeterColors);
945 RadioMenuElem (mode_group, _("Channel Colors"),
946 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
947 ChannelColors, false, true, true)));
948 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
949 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
952 RadioMenuElem (mode_group, _("Track Color"),
953 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
954 TrackColor, false, true, true)));
955 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
956 _channel_color_mode_item->set_active(_color_mode == TrackColor);
962 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
964 if (apply_to_selection) {
965 _editor.get_selection().tracks.foreach_midi_time_axis (
966 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
968 if (_note_mode != mode || midi_track()->note_mode() != mode) {
970 midi_track()->set_note_mode(mode);
971 set_gui_property ("note-mode", enum_2_string(_note_mode));
972 _view->redisplay_track();
978 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
980 if (apply_to_selection) {
981 _editor.get_selection().tracks.foreach_midi_time_axis (
982 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
984 if (_color_mode == mode && !force) {
988 if (mode == ChannelColors) {
989 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
991 _channel_selector.set_default_channel_color();
995 set_gui_property ("color-mode", enum_2_string(_color_mode));
997 _view->redisplay_track();
1003 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1005 if (apply_to_selection) {
1006 _editor.get_selection().tracks.foreach_midi_time_axis (
1007 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1009 if (!_ignore_signals) {
1010 midi_view()->set_note_range(range);
1016 MidiTimeAxisView::update_range()
1018 MidiGhostRegion* mgr;
1020 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1021 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1022 mgr->update_range();
1028 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1030 if (apply_to_selection) {
1031 _editor.get_selection().tracks.foreach_midi_time_axis (
1032 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1035 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1037 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1038 create_automation_child(*i, true);
1042 RouteTimeAxisView::show_all_automation ();
1047 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1049 if (apply_to_selection) {
1050 _editor.get_selection().tracks.foreach_midi_time_axis (
1051 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1054 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1056 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1057 create_automation_child (*i, true);
1061 RouteTimeAxisView::show_existing_automation ();
1065 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1068 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1070 if (param.type() == NullAutomation) {
1074 AutomationTracks::iterator existing = _automation_tracks.find (param);
1076 if (existing != _automation_tracks.end()) {
1078 /* automation track created because we had existing data for
1079 * the processor, but visibility may need to be controlled
1080 * since it will have been set visible by default.
1083 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1090 boost::shared_ptr<AutomationTimeAxisView> track;
1092 switch (param.type()) {
1094 case GainAutomation:
1095 create_gain_automation_child (param, show);
1098 case PluginAutomation:
1099 /* handled elsewhere */
1102 case MidiCCAutomation:
1103 case MidiPgmChangeAutomation:
1104 case MidiPitchBenderAutomation:
1105 case MidiChannelPressureAutomation:
1106 case MidiSystemExclusiveAutomation:
1107 /* These controllers are region "automation" - they are owned
1108 * by regions (and their MidiModels), not by the track. As a
1109 * result we do not create an AutomationList/Line for the track
1110 * ... except here we are doing something!! XXX
1113 track.reset (new AutomationTimeAxisView (
1116 boost::shared_ptr<Automatable> (),
1117 boost::shared_ptr<AutomationControl> (),
1123 _route->describe_parameter(param)));
1126 _view->foreach_regionview (
1127 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1130 add_automation_child (param, track, show);
1134 error << "MidiTimeAxisView: unknown automation child "
1135 << EventTypeMap::instance().to_symbol(param) << endmsg;
1140 MidiTimeAxisView::route_active_changed ()
1142 RouteUI::route_active_changed ();
1145 if (_route->active()) {
1146 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1147 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1148 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1150 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1151 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1152 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1155 if (_route->active()) {
1156 controls_ebox.set_name ("BusControlsBaseUnselected");
1157 controls_base_selected_name = "BusControlsBaseSelected";
1158 controls_base_unselected_name = "BusControlsBaseUnselected";
1160 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1161 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1162 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1168 MidiTimeAxisView::set_note_selection (uint8_t note)
1170 if (!_editor.internal_editing()) {
1174 uint16_t chn_mask = _channel_selector.get_selected_channels();
1176 if (_view->num_selected_regionviews() == 0) {
1177 _view->foreach_regionview (
1178 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1181 _view->foreach_selected_regionview (
1182 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1188 MidiTimeAxisView::add_note_selection (uint8_t note)
1190 if (!_editor.internal_editing()) {
1194 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1196 if (_view->num_selected_regionviews() == 0) {
1197 _view->foreach_regionview (
1198 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1201 _view->foreach_selected_regionview (
1202 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1208 MidiTimeAxisView::extend_note_selection (uint8_t note)
1210 if (!_editor.internal_editing()) {
1214 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1216 if (_view->num_selected_regionviews() == 0) {
1217 _view->foreach_regionview (
1218 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1221 _view->foreach_selected_regionview (
1222 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1228 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1230 if (!_editor.internal_editing()) {
1234 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1236 if (_view->num_selected_regionviews() == 0) {
1237 _view->foreach_regionview (
1238 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1241 _view->foreach_selected_regionview (
1242 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1248 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1250 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1254 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1256 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1260 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1262 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1266 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1268 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1272 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1274 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1278 const uint16_t selected_channels = _channel_selector.get_selected_channels();
1279 bool changed = false;
1283 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1285 for (uint32_t chn = 0; chn < 16; ++chn) {
1286 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1287 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1293 if ((selected_channels & (0x0001 << chn)) == 0) {
1294 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1295 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1297 changed = track->set_marked_for_display (false) || changed;
1299 changed = track->set_marked_for_display (true) || changed;
1306 /* TODO: Bender, Pressure */
1308 /* invalidate the controller menu, so that we rebuild it next time */
1309 _controller_menu_map.clear ();
1310 delete controller_menu;
1311 controller_menu = 0;
1319 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1321 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1326 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1327 if (i != _controller_menu_map.end()) {
1331 i = _channel_command_menu_map.find (param);
1332 if (i != _channel_command_menu_map.end()) {
1339 boost::shared_ptr<MidiRegion>
1340 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1342 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1344 real_editor->begin_reversible_command (Operations::create_region);
1345 playlist()->clear_changes ();
1347 real_editor->snap_to (pos, 0);
1349 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1350 view()->trackview().track().get(), view()->trackview().track()->name());
1353 plist.add (ARDOUR::Properties::start, 0);
1354 plist.add (ARDOUR::Properties::length, length);
1355 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1357 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1359 playlist()->add_region (region, pos);
1360 _session->add_command (new StatefulDiffCommand (playlist()));
1363 real_editor->commit_reversible_command ();
1366 return boost::dynamic_pointer_cast<MidiRegion>(region);
1370 MidiTimeAxisView::ensure_step_editor ()
1372 if (!_step_editor) {
1373 _step_editor = new StepEditor (_editor, midi_track(), *this);
1378 MidiTimeAxisView::start_step_editing ()
1380 ensure_step_editor ();
1381 _step_editor->start_step_editing ();
1385 MidiTimeAxisView::stop_step_editing ()
1388 _step_editor->stop_step_editing ();
1392 /** @return channel (counted from 0) to add an event to, based on the current setting
1393 * of the channel selector.
1396 MidiTimeAxisView::get_channel_for_add () const
1398 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1400 uint8_t channel = 0;
1402 /* pick the highest selected channel, unless all channels are selected,
1403 which is interpreted to mean channel 1 (zero)
1406 for (uint16_t i = 0; i < 16; ++i) {
1407 if (chn_mask & (1<<i)) {
1413 if (chn_cnt == 16) {
1421 MidiTimeAxisView::note_range_changed ()
1423 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1424 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1428 MidiTimeAxisView::contents_height_changed ()
1430 _range_scroomer->set_size_request (-1, _view->child_height ());