2 Copyright (C) 2000 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <sigc++/bind.h>
28 #include "pbd/error.h"
29 #include "pbd/stl_delete.h"
30 #include "pbd/whitespace.h"
31 #include "pbd/basename.h"
32 #include "pbd/enumwriter.h"
33 #include "pbd/memento_command.h"
34 #include "pbd/stateful_diff_command.h"
36 #include "gtkmm2ext/gtk_ui.h"
37 #include "gtkmm2ext/selector.h"
38 #include "gtkmm2ext/bindable_button.h"
39 #include "gtkmm2ext/utils.h"
41 #include "ardour/event_type_map.h"
42 #include "ardour/midi_patch_manager.h"
43 #include "ardour/midi_playlist.h"
44 #include "ardour/midi_region.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/midi_track.h"
47 #include "ardour/operations.h"
48 #include "ardour/playlist.h"
49 #include "ardour/region.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/route.h"
52 #include "ardour/session.h"
53 #include "ardour/session_object.h"
54 #include "ardour/source.h"
55 #include "ardour/track.h"
56 #include "ardour/types.h"
58 #include "midi++/names.h"
60 #include "ardour_ui.h"
61 #include "ardour_button.h"
62 #include "automation_line.h"
63 #include "automation_time_axis.h"
64 #include "canvas-note-event.h"
65 #include "canvas_impl.h"
68 #include "ghostregion.h"
69 #include "gui_thread.h"
71 #include "midi_scroomer.h"
72 #include "midi_streamview.h"
73 #include "midi_region_view.h"
74 #include "midi_time_axis.h"
75 #include "piano_roll_header.h"
76 #include "playlist_selector.h"
77 #include "plugin_selector.h"
78 #include "plugin_ui.h"
79 #include "point_selection.h"
81 #include "region_view.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "step_editor.h"
85 #include "simplerect.h"
88 #include "ardour/midi_track.h"
92 using namespace ARDOUR;
95 using namespace Gtkmm2ext;
96 using namespace Editing;
98 // Minimum height at which a control is displayed
99 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
100 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
102 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
103 : AxisView(sess) // virtually inherited
104 , RouteTimeAxisView(ed, sess, canvas)
105 , _ignore_signals(false)
107 , _piano_roll_header(0)
108 , _note_mode(Sustained)
110 , _percussion_mode_item(0)
111 , _color_mode(MeterColors)
112 , _meter_color_mode_item(0)
113 , _channel_color_mode_item(0)
114 , _track_color_mode_item(0)
115 , _step_edit_item (0)
116 , controller_menu (0)
122 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
126 _view = new MidiStreamView (*this);
129 _piano_roll_header = new PianoRollHeader(*midi_view());
130 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
131 _range_scroomer->DoubleClicked.connect (
132 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
133 MidiStreamView::ContentsRange, false));
136 /* This next call will result in our height being set up, so it must come after
137 the creation of the piano roll / range scroomer as their visibility is set up
140 RouteTimeAxisView::set_route (rt);
142 _view->apply_color (_color, StreamView::RegionColor);
144 subplugin_menu.set_name ("ArdourContextMenu");
146 if (!gui_property ("note-range-min").empty ()) {
147 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
148 atoi (gui_property ("note-range-max").c_str()),
151 midi_view()->NoteRangeChanged.connect (
152 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
153 _view->ContentsHeightChanged.connect (
154 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
156 ignore_toggle = false;
158 if (is_midi_track()) {
159 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
160 _note_mode = midi_track()->note_mode();
161 } else { // MIDI bus (which doesn't exist yet..)
162 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
165 /* map current state of the route */
167 processors_changed (RouteProcessorChange ());
169 _route->processors_changed.connect (*this, invalidator (*this),
170 boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
174 _piano_roll_header->SetNoteSelection.connect (
175 sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
176 _piano_roll_header->AddNoteSelection.connect (
177 sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
178 _piano_roll_header->ExtendNoteSelection.connect (
179 sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
180 _piano_roll_header->ToggleNoteSelection.connect (
181 sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
183 /* Suspend updates of the StreamView during scroomer drags to speed things up */
184 _range_scroomer->DragStarting.connect (
185 sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
186 _range_scroomer->DragFinishing.connect (
187 sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
189 /* Put the scroomer and the keyboard in a VBox with a padding
190 label so that they can be reduced in height for stacked-view
193 VBox* v = manage (new VBox);
194 HBox* h = manage (new HBox);
195 h->pack_start (*_range_scroomer);
196 h->pack_start (*_piano_roll_header);
197 v->pack_start (*h, false, false);
198 v->pack_start (*manage (new Label ("")), true, true);
201 controls_hbox.pack_start(*v);
203 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
204 controls_base_selected_name = "MidiTrackControlsBaseSelected";
205 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
207 midi_view()->NoteRangeChanged.connect (
208 sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
210 /* ask for notifications of any new RegionViews */
211 _view->RegionViewAdded.connect (
212 sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
214 if (!_editor.have_idled()) {
215 /* 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 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
231 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
233 _midnam_model_selector.signal_changed().connect(
234 sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
235 _midnam_custom_device_mode_selector.signal_changed().connect(
236 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
238 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
239 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
241 _midi_controls_box.set_homogeneous(false);
242 _midi_controls_box.set_border_width (10);
243 if (!patch_manager.all_models().empty()) {
244 _channel_selector.set_border_width(2);
245 _midi_controls_box.resize(3, 2);
246 _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1);
248 _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2);
250 _midnam_model_selector.set_size_request(22, 30);
251 _midnam_model_selector.set_border_width(2);
252 _midi_controls_box.attach(_midnam_model_selector, 0, 1, 2, 3);
254 _midnam_custom_device_mode_selector.set_size_request(10, 30);
255 _midnam_custom_device_mode_selector.set_border_width(2);
256 _midi_controls_box.attach(_midnam_custom_device_mode_selector, 0, 1, 3, 4);
258 _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1);
261 controls_vbox.pack_start(_midi_controls_box, false, false);
263 // restore channel selector settings
264 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(),
265 midi_track()->get_channel_mask());
266 _channel_selector.mode_changed.connect(
267 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
268 _channel_selector.mode_changed.connect(
269 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
271 const string color_mode = gui_property ("color-mode");
272 if (!color_mode.empty()) {
273 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
274 if (_color_mode == ChannelColors) {
275 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
279 set_color_mode (_color_mode, true, false);
281 const string note_mode = gui_property ("note-mode");
282 if (!note_mode.empty()) {
283 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
284 if (_percussion_mode_item) {
285 _percussion_mode_item->set_active (_note_mode == Percussive);
289 /* Look for any GUI object state nodes that represent automation children
290 * that should exist, and create the children.
293 const list<string> gui_ids = gui_object_state().all_ids ();
294 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
297 Evoral::Parameter parameter (0, 0, 0);
299 bool const p = AutomationTimeAxisView::parse_state_id (
300 *i, route_id, has_parameter, parameter);
301 if (p && route_id == _route->id () && has_parameter) {
302 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
303 create_automation_child (parameter, string_is_affirmative (visible));
309 MidiTimeAxisView::first_idle ()
316 MidiTimeAxisView::~MidiTimeAxisView ()
318 delete _piano_roll_header;
319 _piano_roll_header = 0;
321 delete _range_scroomer;
324 delete controller_menu;
329 MidiTimeAxisView::enter_internal_edit_mode ()
332 midi_view()->enter_internal_edit_mode ();
337 MidiTimeAxisView::leave_internal_edit_mode ()
340 midi_view()->leave_internal_edit_mode ();
345 MidiTimeAxisView::check_step_edit ()
347 ensure_step_editor ();
348 _step_editor->check_step_edit ();
352 MidiTimeAxisView::model_changed()
354 const Glib::ustring model = _midnam_model_selector.get_active_text();
355 set_gui_property (X_("midnam-model-name"), model);
357 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
358 .custom_device_mode_names_by_model(model);
360 _midnam_custom_device_mode_selector.clear_items();
362 if (device_modes.size() < 2) {
363 _midnam_custom_device_mode_selector.hide();
365 _midnam_custom_device_mode_selector.show();
366 for (std::list<std::string>::const_iterator i = device_modes.begin();
367 i != device_modes.end(); ++i) {
368 _midnam_custom_device_mode_selector.append_text(*i);
372 _midnam_custom_device_mode_selector.set_active(0);
374 _route->instrument_info().set_external_instrument (
375 _midnam_model_selector.get_active_text(),
376 _midnam_custom_device_mode_selector.get_active_text());
378 // Rebuild controller menu
379 _controller_menu_map.clear ();
380 delete controller_menu;
382 build_automation_action_menu(false);
386 MidiTimeAxisView::custom_device_mode_changed()
388 const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text();
389 set_gui_property (X_("midnam-custom-device-mode"), mode);
390 _route->instrument_info().set_external_instrument (
391 _midnam_model_selector.get_active_text(), mode);
395 MidiTimeAxisView::midi_view()
397 return dynamic_cast<MidiStreamView*>(_view);
401 MidiTimeAxisView::set_height (uint32_t h)
403 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
404 _midi_controls_box.show ();
406 _midi_controls_box.hide();
409 if (h >= KEYBOARD_MIN_HEIGHT) {
410 if (is_track() && _range_scroomer) {
411 _range_scroomer->show();
413 if (is_track() && _piano_roll_header) {
414 _piano_roll_header->show();
417 if (is_track() && _range_scroomer) {
418 _range_scroomer->hide();
420 if (is_track() && _piano_roll_header) {
421 _piano_roll_header->hide();
425 /* We need to do this after changing visibility of our stuff, as it will
426 eventually trigger a call to Editor::reset_controls_layout_width(),
427 which needs to know if we have just shown or hidden a scroomer /
430 RouteTimeAxisView::set_height (h);
434 MidiTimeAxisView::append_extra_display_menu_items ()
436 using namespace Menu_Helpers;
438 MenuList& items = display_menu->items();
441 Menu *range_menu = manage(new Menu);
442 MenuList& range_items = range_menu->items();
443 range_menu->set_name ("ArdourContextMenu");
445 range_items.push_back (
446 MenuElem (_("Show Full Range"),
447 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
448 MidiStreamView::FullRange, true)));
450 range_items.push_back (
451 MenuElem (_("Fit Contents"),
452 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
453 MidiStreamView::ContentsRange, true)));
455 items.push_back (MenuElem (_("Note Range"), *range_menu));
456 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
458 items.push_back (SeparatorElem ());
462 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
464 using namespace Menu_Helpers;
466 /* If we have a controller menu, we need to detach it before
467 RouteTimeAxis::build_automation_action_menu destroys the
468 menu it is attached to. Otherwise GTK destroys
469 controller_menu's gobj, meaning that it can't be reattached
470 below. See bug #3134.
473 if (controller_menu) {
474 detach_menu (*controller_menu);
477 _channel_command_menu_map.clear ();
478 RouteTimeAxisView::build_automation_action_menu (for_selection);
480 MenuList& automation_items = automation_action_menu->items();
482 uint16_t selected_channels = _channel_selector.get_selected_channels();
484 if (selected_channels != 0) {
486 automation_items.push_back (SeparatorElem());
488 /* these 2 MIDI "command" types are semantically more like automation
489 than note data, but they are not MIDI controllers. We give them
490 special status in this menu, since they will not show up in the
491 controller list and anyone who actually knows something about MIDI
492 (!) would not expect to find them there.
495 add_channel_command_menu_item (
496 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
497 automation_items.back().set_sensitive (
498 !for_selection || _editor.get_selection().tracks.size() == 1);
499 add_channel_command_menu_item (
500 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
501 automation_items.back().set_sensitive (
502 !for_selection || _editor.get_selection().tracks.size() == 1);
504 /* now all MIDI controllers. Always offer the possibility that we will
505 rebuild the controllers menu since it might need to be updated after
506 a channel mode change or other change. Also detach it first in case
507 it has been used anywhere else.
510 build_controller_menu ();
512 automation_items.push_back (SeparatorElem());
513 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
514 automation_items.back().set_sensitive (
515 !for_selection || _editor.get_selection().tracks.size() == 1);
517 automation_items.push_back (
518 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
519 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
524 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
526 const uint16_t selected_channels = _channel_selector.get_selected_channels();
528 for (uint8_t chn = 0; chn < 16; chn++) {
529 if (selected_channels & (0x0001 << chn)) {
531 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
532 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
535 menu->set_active (yn);
542 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
544 AutomationType auto_type,
547 using namespace Menu_Helpers;
549 /* count the number of selected channels because we will build a different menu
550 structure if there is more than 1 selected.
553 const uint16_t selected_channels = _channel_selector.get_selected_channels();
556 for (uint8_t chn = 0; chn < 16; chn++) {
557 if (selected_channels & (0x0001 << chn)) {
566 /* multiple channels - create a submenu, with 1 item per channel */
568 Menu* chn_menu = manage (new Menu);
569 MenuList& chn_items (chn_menu->items());
570 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
572 /* add a couple of items to hide/show all of them */
574 chn_items.push_back (
575 MenuElem (_("Hide all channels"),
576 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
577 false, param_without_channel)));
578 chn_items.push_back (
579 MenuElem (_("Show all channels"),
580 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
581 true, param_without_channel)));
583 for (uint8_t chn = 0; chn < 16; chn++) {
584 if (selected_channels & (0x0001 << chn)) {
586 /* for each selected channel, add a menu item for this controller */
588 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
589 chn_items.push_back (
590 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
591 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
592 fully_qualified_param)));
594 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
595 bool visible = false;
598 if (track->marked_for_display()) {
603 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
604 _channel_command_menu_map[fully_qualified_param] = cmi;
605 cmi->set_active (visible);
609 /* now create an item in the parent menu that has the per-channel list as a submenu */
611 items.push_back (MenuElem (label, *chn_menu));
615 /* just one channel - create a single menu item for this command+channel combination*/
617 for (uint8_t chn = 0; chn < 16; chn++) {
618 if (selected_channels & (0x0001 << chn)) {
620 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
622 CheckMenuElem (label,
623 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
624 fully_qualified_param)));
626 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
627 bool visible = false;
630 if (track->marked_for_display()) {
635 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
636 _channel_command_menu_map[fully_qualified_param] = cmi;
637 cmi->set_active (visible);
639 /* one channel only */
647 MidiTimeAxisView::build_controller_menu ()
649 using namespace Menu_Helpers;
651 if (controller_menu) {
652 /* it exists and has not been invalidated by a channel mode change */
656 controller_menu = new Menu; // explicitly managed by us
657 MenuList& items (controller_menu->items());
659 /* create several "top level" menu items for sets of controllers (16 at a
660 time), and populate each one with a submenu for each controller+channel
661 combination covering the currently selected channels for this track
664 const uint16_t selected_channels = _channel_selector.get_selected_channels();
666 /* count the number of selected channels because we will build a different menu
667 structure if there is more than 1 selected.
671 for (uint8_t chn = 0; chn < 16; chn++) {
672 if (selected_channels & (0x0001 << chn)) {
679 using namespace MIDI::Name;
680 const Glib::ustring model = _midnam_model_selector.get_active_text();
681 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
682 .document_by_model(model);
683 boost::shared_ptr<MasterDeviceNames> device_names;
684 if (midnam && !midnam->master_device_names_by_model().empty()) {
685 device_names = boost::shared_ptr<MasterDeviceNames>(
686 midnam->master_device_names_by_model().begin()->second);
689 if (device_names && !device_names->controls().empty()) {
690 /* Controllers names available from the midnam file,
691 generate a custom controller menu */
692 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
693 l != device_names->controls().end(); ++l) {
694 boost::shared_ptr<ControlNameList> name_list = *l;
697 Menu* group_menu = manage(new Menu());
698 MenuList& group_items(group_menu->items());
700 for (ControlNameList::Controls::const_iterator c = (*l)->controls().begin();
701 c != (*l)->controls().end(); ++c) {
702 Evoral::Parameter fully_qualified_param(MidiCCAutomation, chn, atoi((*c)->number().c_str()));
703 group_items.push_back(
704 CheckMenuElem(string_compose("<b>%1</b>: %2 [%3]",
705 (*c)->number(), (*c)->name(), int(chn)),
707 sigc::mem_fun(*this, &RouteTimeAxisView::toggle_automation_track),
708 fully_qualified_param)));
709 dynamic_cast<Label*> (group_items.back().get_child())->set_use_markup (true);
711 items.push_back(MenuElem(name_list->name(), *group_menu));
716 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
717 bank select controllers, as they are handled by the `patch' code */
719 for (int i = 0; i < 127; i += 16) {
721 Menu* ctl_menu = manage (new Menu);
722 MenuList& ctl_items (ctl_menu->items());
724 /* for each controller, consider whether to create a submenu or a single item */
726 for (int ctl = i; ctl < i+16; ++ctl) {
728 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
734 /* multiple channels - create a submenu, with 1 item per channel */
736 Menu* chn_menu = manage (new Menu);
737 MenuList& chn_items (chn_menu->items());
739 /* add a couple of items to hide/show this controller on all channels */
741 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
742 chn_items.push_back (
743 MenuElem (_("Hide all channels"),
744 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
745 false, param_without_channel)));
746 chn_items.push_back (
747 MenuElem (_("Show all channels"),
748 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
749 true, param_without_channel)));
751 for (uint8_t chn = 0; chn < 16; chn++) {
752 if (selected_channels & (0x0001 << chn)) {
754 /* for each selected channel, add a menu item for this controller */
756 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
757 chn_items.push_back (
758 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
759 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
760 fully_qualified_param)));
762 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
763 fully_qualified_param);
764 bool visible = false;
767 if (track->marked_for_display()) {
772 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
773 _controller_menu_map[fully_qualified_param] = cmi;
774 cmi->set_active (visible);
778 /* add the per-channel menu to the list of controllers, with the name of the controller */
779 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)),
781 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
785 /* just one channel - create a single menu item for this ctl+channel combination */
787 for (uint8_t chn = 0; chn < 16; chn++) {
788 if (selected_channels & (0x0001 << chn)) {
790 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
791 ctl_items.push_back (
793 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
795 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
796 fully_qualified_param)));
797 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
799 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
800 fully_qualified_param);
802 bool visible = false;
804 if (track->marked_for_display()) {
809 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
810 _controller_menu_map[fully_qualified_param] = cmi;
811 cmi->set_active (visible);
813 /* one channel only */
820 /* add the menu for this block of controllers to the overall controller menu */
822 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
827 MidiTimeAxisView::build_note_mode_menu()
829 using namespace Menu_Helpers;
831 Menu* mode_menu = manage (new Menu);
832 MenuList& items = mode_menu->items();
833 mode_menu->set_name ("ArdourContextMenu");
835 RadioMenuItem::Group mode_group;
837 RadioMenuElem (mode_group,_("Sustained"),
838 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
840 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
841 _note_mode_item->set_active(_note_mode == Sustained);
844 RadioMenuElem (mode_group, _("Percussive"),
845 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
847 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
848 _percussion_mode_item->set_active(_note_mode == Percussive);
854 MidiTimeAxisView::build_color_mode_menu()
856 using namespace Menu_Helpers;
858 Menu* mode_menu = manage (new Menu);
859 MenuList& items = mode_menu->items();
860 mode_menu->set_name ("ArdourContextMenu");
862 RadioMenuItem::Group mode_group;
864 RadioMenuElem (mode_group, _("Meter Colors"),
865 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
866 MeterColors, false, true, true)));
867 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
868 _meter_color_mode_item->set_active(_color_mode == MeterColors);
871 RadioMenuElem (mode_group, _("Channel Colors"),
872 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
873 ChannelColors, false, true, true)));
874 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
875 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
878 RadioMenuElem (mode_group, _("Track Color"),
879 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
880 TrackColor, false, true, true)));
881 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
882 _channel_color_mode_item->set_active(_color_mode == TrackColor);
888 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
890 if (apply_to_selection) {
891 _editor.get_selection().tracks.foreach_midi_time_axis (
892 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
894 if (_note_mode != mode || midi_track()->note_mode() != mode) {
896 midi_track()->set_note_mode(mode);
897 set_gui_property ("note-mode", enum_2_string(_note_mode));
898 _view->redisplay_track();
904 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
906 if (apply_to_selection) {
907 _editor.get_selection().tracks.foreach_midi_time_axis (
908 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
910 if (_color_mode == mode && !force) {
914 if (mode == ChannelColors) {
915 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
917 _channel_selector.set_default_channel_color();
921 set_gui_property ("color-mode", enum_2_string(_color_mode));
923 _view->redisplay_track();
929 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
931 if (apply_to_selection) {
932 _editor.get_selection().tracks.foreach_midi_time_axis (
933 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
935 if (!_ignore_signals) {
936 midi_view()->set_note_range(range);
942 MidiTimeAxisView::update_range()
944 MidiGhostRegion* mgr;
946 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
947 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
954 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
956 if (apply_to_selection) {
957 _editor.get_selection().tracks.foreach_midi_time_axis (
958 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
961 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
963 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
964 create_automation_child(*i, true);
968 RouteTimeAxisView::show_all_automation ();
973 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
975 if (apply_to_selection) {
976 _editor.get_selection().tracks.foreach_midi_time_axis (
977 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
980 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
982 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
983 create_automation_child (*i, true);
987 RouteTimeAxisView::show_existing_automation ();
991 /** Create an automation track for the given parameter (pitch bend, channel pressure).
994 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
996 if (param.type() == NullAutomation) {
1000 AutomationTracks::iterator existing = _automation_tracks.find (param);
1002 if (existing != _automation_tracks.end()) {
1004 /* automation track created because we had existing data for
1005 * the processor, but visibility may need to be controlled
1006 * since it will have been set visible by default.
1009 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1016 boost::shared_ptr<AutomationTimeAxisView> track;
1018 switch (param.type()) {
1020 case GainAutomation:
1021 create_gain_automation_child (param, show);
1024 case PluginAutomation:
1025 /* handled elsewhere */
1028 case MidiCCAutomation:
1029 case MidiPgmChangeAutomation:
1030 case MidiPitchBenderAutomation:
1031 case MidiChannelPressureAutomation:
1032 case MidiSystemExclusiveAutomation:
1033 /* These controllers are region "automation" - they are owned
1034 * by regions (and their MidiModels), not by the track. As a
1035 * result we do not create an AutomationList/Line for the track
1036 * ... except here we are doing something!! XXX
1039 track.reset (new AutomationTimeAxisView (
1042 boost::shared_ptr<Automatable> (),
1043 boost::shared_ptr<AutomationControl> (),
1049 _route->describe_parameter(param)));
1052 _view->foreach_regionview (
1053 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1056 add_automation_child (param, track, show);
1060 error << "MidiTimeAxisView: unknown automation child "
1061 << EventTypeMap::instance().to_symbol(param) << endmsg;
1066 MidiTimeAxisView::route_active_changed ()
1068 RouteUI::route_active_changed ();
1071 if (_route->active()) {
1072 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1073 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1074 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1076 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1077 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1078 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1081 if (_route->active()) {
1082 controls_ebox.set_name ("BusControlsBaseUnselected");
1083 controls_base_selected_name = "BusControlsBaseSelected";
1084 controls_base_unselected_name = "BusControlsBaseUnselected";
1086 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1087 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1088 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1094 MidiTimeAxisView::set_note_selection (uint8_t note)
1096 if (!_editor.internal_editing()) {
1100 uint16_t chn_mask = _channel_selector.get_selected_channels();
1102 if (_view->num_selected_regionviews() == 0) {
1103 _view->foreach_regionview (
1104 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1107 _view->foreach_selected_regionview (
1108 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1114 MidiTimeAxisView::add_note_selection (uint8_t note)
1116 if (!_editor.internal_editing()) {
1120 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1122 if (_view->num_selected_regionviews() == 0) {
1123 _view->foreach_regionview (
1124 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1127 _view->foreach_selected_regionview (
1128 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1134 MidiTimeAxisView::extend_note_selection (uint8_t note)
1136 if (!_editor.internal_editing()) {
1140 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1142 if (_view->num_selected_regionviews() == 0) {
1143 _view->foreach_regionview (
1144 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1147 _view->foreach_selected_regionview (
1148 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1154 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1156 if (!_editor.internal_editing()) {
1160 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1162 if (_view->num_selected_regionviews() == 0) {
1163 _view->foreach_regionview (
1164 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1167 _view->foreach_selected_regionview (
1168 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1174 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1176 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1180 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1182 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1186 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1188 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1192 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1194 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1198 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1200 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1204 const uint16_t selected_channels = _channel_selector.get_selected_channels();
1205 bool changed = false;
1209 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1211 for (uint32_t chn = 0; chn < 16; ++chn) {
1212 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1213 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1219 if ((selected_channels & (0x0001 << chn)) == 0) {
1220 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1221 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1223 changed = track->set_marked_for_display (false) || changed;
1225 changed = track->set_marked_for_display (true) || changed;
1232 /* TODO: Bender, Pressure */
1234 /* invalidate the controller menu, so that we rebuild it next time */
1235 _controller_menu_map.clear ();
1236 delete controller_menu;
1237 controller_menu = 0;
1245 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1247 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1252 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1253 if (i != _controller_menu_map.end()) {
1257 i = _channel_command_menu_map.find (param);
1258 if (i != _channel_command_menu_map.end()) {
1265 boost::shared_ptr<MidiRegion>
1266 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1268 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1270 real_editor->begin_reversible_command (Operations::create_region);
1271 playlist()->clear_changes ();
1273 real_editor->snap_to (pos, 0);
1275 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1276 view()->trackview().track().get(), view()->trackview().track()->name());
1279 plist.add (ARDOUR::Properties::start, 0);
1280 plist.add (ARDOUR::Properties::length, length);
1281 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1283 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1285 playlist()->add_region (region, pos);
1286 _session->add_command (new StatefulDiffCommand (playlist()));
1289 real_editor->commit_reversible_command ();
1292 return boost::dynamic_pointer_cast<MidiRegion>(region);
1296 MidiTimeAxisView::ensure_step_editor ()
1298 if (!_step_editor) {
1299 _step_editor = new StepEditor (_editor, midi_track(), *this);
1304 MidiTimeAxisView::start_step_editing ()
1306 ensure_step_editor ();
1307 _step_editor->start_step_editing ();
1311 MidiTimeAxisView::stop_step_editing ()
1314 _step_editor->stop_step_editing ();
1319 /** @return channel (counted from 0) to add an event to, based on the current setting
1320 * of the channel selector.
1323 MidiTimeAxisView::get_channel_for_add () const
1325 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1327 uint8_t channel = 0;
1329 /* pick the highest selected channel, unless all channels are selected,
1330 which is interpreted to mean channel 1 (zero)
1333 for (uint16_t i = 0; i < 16; ++i) {
1334 if (chn_mask & (1<<i)) {
1340 if (chn_cnt == 16) {
1348 MidiTimeAxisView::note_range_changed ()
1350 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1351 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1355 MidiTimeAxisView::contents_height_changed ()
1357 _range_scroomer->set_size_request (-1, _view->child_height ());