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 (sigc::bind (
132 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()), atoi (gui_property ("note-range-max").c_str()), true);
149 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
150 _view->ContentsHeightChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
152 ignore_toggle = false;
154 if (is_midi_track()) {
155 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
156 _note_mode = midi_track()->note_mode();
157 } else { // MIDI bus (which doesn't exist yet..)
158 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
161 /* map current state of the route */
163 processors_changed (RouteProcessorChange ());
165 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
168 _piano_roll_header->SetNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
169 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
170 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
171 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
173 /* Suspend updates of the StreamView during scroomer drags to speed things up */
174 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
175 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
177 /* Put the scroomer and the keyboard in a VBox with a padding
178 label so that they can be reduced in height for stacked-view
181 VBox* v = manage (new VBox);
182 HBox* h = manage (new HBox);
183 h->pack_start (*_range_scroomer);
184 h->pack_start (*_piano_roll_header);
185 v->pack_start (*h, false, false);
186 v->pack_start (*manage (new Label ("")), true, true);
189 controls_hbox.pack_start(*v);
191 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
192 controls_base_selected_name = "MidiTrackControlsBaseSelected";
193 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
195 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
197 /* ask for notifications of any new RegionViews */
198 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
200 if (!_editor.have_idled()) {
201 /* first idle will do what we need */
208 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
210 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
211 for (; m != patch_manager.all_models().end(); ++m) {
212 _midnam_model_selector.append_text(m->c_str());
215 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
217 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
219 _midnam_model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
220 _midnam_custom_device_mode_selector.signal_changed().connect(
221 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
223 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
224 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
226 _midi_controls_box.set_homogeneous(false);
227 _midi_controls_box.set_border_width (10);
228 if (!patch_manager.all_models().empty()) {
229 _channel_selector.set_border_width(2);
230 _midi_controls_box.resize(3, 2);
231 _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1);
233 _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2);
235 _midnam_model_selector.set_size_request(22, 30);
236 _midnam_model_selector.set_border_width(2);
237 _midi_controls_box.attach(_midnam_model_selector, 0, 1, 2, 3);
239 _midnam_custom_device_mode_selector.set_size_request(10, 30);
240 _midnam_custom_device_mode_selector.set_border_width(2);
241 _midi_controls_box.attach(_midnam_custom_device_mode_selector, 1, 2, 2, 3);
243 _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1);
246 controls_vbox.pack_start(_midi_controls_box, false, false);
248 // restore channel selector settings
249 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
250 _channel_selector.mode_changed.connect(
251 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
252 _channel_selector.mode_changed.connect(
253 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
255 string prop = gui_property ("color-mode");
257 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
258 if (_color_mode == ChannelColors) {
259 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
263 set_color_mode (_color_mode, true, false);
265 prop = gui_property ("note-mode");
267 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
268 if (_percussion_mode_item) {
269 _percussion_mode_item->set_active (_note_mode == Percussive);
273 /* Look for any GUI object state nodes that represent automation children that should exist, and create
277 list<string> gui_ids = gui_object_state().all_ids ();
278 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
281 Evoral::Parameter parameter (0, 0, 0);
283 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
284 if (p && route_id == _route->id () && has_parameter) {
285 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
291 MidiTimeAxisView::first_idle ()
298 MidiTimeAxisView::~MidiTimeAxisView ()
300 delete _piano_roll_header;
301 _piano_roll_header = 0;
303 delete _range_scroomer;
306 delete controller_menu;
311 MidiTimeAxisView::enter_internal_edit_mode ()
314 midi_view()->enter_internal_edit_mode ();
319 MidiTimeAxisView::leave_internal_edit_mode ()
322 midi_view()->leave_internal_edit_mode ();
327 MidiTimeAxisView::check_step_edit ()
329 ensure_step_editor ();
330 _step_editor->check_step_edit ();
334 MidiTimeAxisView::model_changed()
336 string model = _midnam_model_selector.get_active_text();
337 set_gui_property (X_("midnam-model-name"), model);
339 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
340 .custom_device_mode_names_by_model(model);
342 _midnam_custom_device_mode_selector.clear_items();
344 for (std::list<std::string>::const_iterator i = device_modes.begin();
345 i != device_modes.end(); ++i) {
346 _midnam_custom_device_mode_selector.append_text(*i);
349 _midnam_custom_device_mode_selector.set_active(0);
351 _route->instrument_info().set_external_instrument (_midnam_model_selector.get_active_text(), _midnam_custom_device_mode_selector.get_active_text());
355 MidiTimeAxisView::custom_device_mode_changed()
357 string mode = _midnam_custom_device_mode_selector.get_active_text();
358 set_gui_property (X_("midnam-custom-device-mode"), mode);
359 _route->instrument_info().set_external_instrument (_midnam_model_selector.get_active_text(), mode);
363 MidiTimeAxisView::midi_view()
365 return dynamic_cast<MidiStreamView*>(_view);
369 MidiTimeAxisView::set_height (uint32_t h)
371 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
372 _midi_controls_box.show_all ();
374 _midi_controls_box.hide();
377 if (h >= KEYBOARD_MIN_HEIGHT) {
378 if (is_track() && _range_scroomer) {
379 _range_scroomer->show();
381 if (is_track() && _piano_roll_header) {
382 _piano_roll_header->show();
385 if (is_track() && _range_scroomer) {
386 _range_scroomer->hide();
388 if (is_track() && _piano_roll_header) {
389 _piano_roll_header->hide();
393 /* We need to do this after changing visibility of our stuff, as it will
394 eventually trigger a call to Editor::reset_controls_layout_width(),
395 which needs to know if we have just shown or hidden a scroomer /
398 RouteTimeAxisView::set_height (h);
402 MidiTimeAxisView::append_extra_display_menu_items ()
404 using namespace Menu_Helpers;
406 MenuList& items = display_menu->items();
409 Menu *range_menu = manage(new Menu);
410 MenuList& range_items = range_menu->items();
411 range_menu->set_name ("ArdourContextMenu");
413 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
414 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
415 MidiStreamView::FullRange, true)));
417 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
418 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
419 MidiStreamView::ContentsRange, true)));
421 items.push_back (MenuElem (_("Note Range"), *range_menu));
422 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
424 items.push_back (SeparatorElem ());
428 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
430 using namespace Menu_Helpers;
432 /* If we have a controller menu, we need to detach it before
433 RouteTimeAxis::build_automation_action_menu destroys the
434 menu it is attached to. Otherwise GTK destroys
435 controller_menu's gobj, meaning that it can't be reattached
436 below. See bug #3134.
439 if (controller_menu) {
440 detach_menu (*controller_menu);
443 _channel_command_menu_map.clear ();
444 RouteTimeAxisView::build_automation_action_menu (for_selection);
446 MenuList& automation_items = automation_action_menu->items();
448 uint16_t selected_channels = _channel_selector.get_selected_channels();
450 if (selected_channels != 0) {
452 automation_items.push_back (SeparatorElem());
454 /* these 2 MIDI "command" types are semantically more like automation than note data,
455 but they are not MIDI controllers. We give them special status in this menu, since
456 they will not show up in the controller list and anyone who actually knows
457 something about MIDI (!) would not expect to find them there.
460 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
461 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
462 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
463 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
465 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
466 since it might need to be updated after a channel mode change or other change. Also detach it
467 first in case it has been used anywhere else.
470 build_controller_menu ();
472 automation_items.push_back (SeparatorElem());
473 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
474 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
476 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
477 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
483 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
485 uint16_t selected_channels = _channel_selector.get_selected_channels();
487 for (uint8_t chn = 0; chn < 16; chn++) {
488 if (selected_channels & (0x0001 << chn)) {
490 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
491 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
494 menu->set_active (yn);
501 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
503 using namespace Menu_Helpers;
505 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
508 uint16_t selected_channels = _channel_selector.get_selected_channels();
511 for (uint8_t chn = 0; chn < 16; chn++) {
512 if (selected_channels & (0x0001 << chn)) {
521 /* multiple channels - create a submenu, with 1 item per channel */
523 Menu* chn_menu = manage (new Menu);
524 MenuList& chn_items (chn_menu->items());
525 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
527 /* add a couple of items to hide/show all of them */
529 chn_items.push_back (MenuElem (_("Hide all channels"),
530 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
531 false, param_without_channel)));
532 chn_items.push_back (MenuElem (_("Show all channels"),
533 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
534 true, param_without_channel)));
536 for (uint8_t chn = 0; chn < 16; chn++) {
537 if (selected_channels & (0x0001 << chn)) {
539 /* for each selected channel, add a menu item for this controller */
541 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
542 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
543 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
544 fully_qualified_param)));
546 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
547 bool visible = false;
550 if (track->marked_for_display()) {
555 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
556 _channel_command_menu_map[fully_qualified_param] = cmi;
557 cmi->set_active (visible);
561 /* now create an item in the parent menu that has the per-channel list as a submenu */
563 items.push_back (MenuElem (label, *chn_menu));
567 /* just one channel - create a single menu item for this command+channel combination*/
569 for (uint8_t chn = 0; chn < 16; chn++) {
570 if (selected_channels & (0x0001 << chn)) {
572 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
573 items.push_back (CheckMenuElem (label,
574 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
575 fully_qualified_param)));
577 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
578 bool visible = false;
581 if (track->marked_for_display()) {
586 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
587 _channel_command_menu_map[fully_qualified_param] = cmi;
588 cmi->set_active (visible);
590 /* one channel only */
598 MidiTimeAxisView::build_controller_menu ()
600 using namespace Menu_Helpers;
602 if (controller_menu) {
603 /* it exists and has not been invalidated by a channel mode change, so just return it */
607 controller_menu = new Menu; // explicitly managed by us
608 MenuList& items (controller_menu->items());
610 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
611 for each controller+channel combination covering the currently selected channels for this track
614 uint16_t selected_channels = _channel_selector.get_selected_channels();
616 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
621 for (uint8_t chn = 0; chn < 16; chn++) {
622 if (selected_channels & (0x0001 << chn)) {
629 using namespace MIDI::Name;
630 const string& model = _midnam_model_selector.get_active_text();
631 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
632 .document_by_model(model);
633 boost::shared_ptr<MasterDeviceNames> device_names;
634 if (midnam && !midnam->master_device_names_by_model().empty()) {
635 device_names = boost::shared_ptr<MasterDeviceNames>(
636 midnam->master_device_names_by_model().begin()->second);
639 if (device_names && !device_names->controls().empty()) {
640 /* Controllers names available from the midnam file, generate a custom controller menu */
641 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
642 l != device_names->controls().end(); ++l) {
643 boost::shared_ptr<ControlNameList> name_list = *l;
646 Menu* group_menu = manage(new Menu());
647 MenuList& group_items(group_menu->items());
649 for (ControlNameList::Controls::const_iterator c = (*l)->controls().begin();
650 c != (*l)->controls().end(); ++c) {
651 Evoral::Parameter fully_qualified_param(MidiCCAutomation, chn, atoi((*c)->number().c_str()));
652 group_items.push_back(
653 CheckMenuElem(string_compose("<b>%1</b>: %2 [%3]",
654 (*c)->number(), (*c)->name(), int(chn)),
655 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::toggle_automation_track),
656 fully_qualified_param)));
657 dynamic_cast<Label*> (group_items.back().get_child())->set_use_markup (true);
659 items.push_back(MenuElem(name_list->name(), *group_menu));
664 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
665 bank select controllers, as they are handled by the `patch' code */
667 for (int i = 0; i < 127; i += 16) {
669 Menu* ctl_menu = manage (new Menu);
670 MenuList& ctl_items (ctl_menu->items());
673 /* for each controller, consider whether to create a submenu or a single item */
675 for (int ctl = i; ctl < i+16; ++ctl) {
677 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
683 /* multiple channels - create a submenu, with 1 item per channel */
685 Menu* chn_menu = manage (new Menu);
686 MenuList& chn_items (chn_menu->items());
688 /* add a couple of items to hide/show this controller on all channels */
690 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
691 chn_items.push_back (MenuElem (_("Hide all channels"),
692 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
693 false, param_without_channel)));
694 chn_items.push_back (MenuElem (_("Show all channels"),
695 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
696 true, param_without_channel)));
698 for (uint8_t chn = 0; chn < 16; chn++) {
699 if (selected_channels & (0x0001 << chn)) {
701 /* for each selected channel, add a menu item for this controller */
703 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
704 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
705 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
706 fully_qualified_param)));
708 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
709 bool visible = false;
712 if (track->marked_for_display()) {
717 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
718 _controller_menu_map[fully_qualified_param] = cmi;
719 cmi->set_active (visible);
723 /* add the per-channel menu to the list of controllers, with the name of the controller */
724 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
725 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
729 /* just one channel - create a single menu item for this ctl+channel combination*/
731 for (uint8_t chn = 0; chn < 16; chn++) {
732 if (selected_channels & (0x0001 << chn)) {
734 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
735 ctl_items.push_back (
737 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
738 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
739 fully_qualified_param)
742 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
744 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
745 bool visible = false;
748 if (track->marked_for_display()) {
753 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
754 _controller_menu_map[fully_qualified_param] = cmi;
755 cmi->set_active (visible);
757 /* one channel only */
764 /* add the menu for this block of controllers to the overall controller menu */
766 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
771 MidiTimeAxisView::build_note_mode_menu()
773 using namespace Menu_Helpers;
775 Menu* mode_menu = manage (new Menu);
776 MenuList& items = mode_menu->items();
777 mode_menu->set_name ("ArdourContextMenu");
779 RadioMenuItem::Group mode_group;
780 items.push_back (RadioMenuElem (mode_group,_("Sustained"),
781 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained, true)));
782 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
783 _note_mode_item->set_active(_note_mode == Sustained);
785 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
786 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive, true)));
787 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
788 _percussion_mode_item->set_active(_note_mode == Percussive);
794 MidiTimeAxisView::build_color_mode_menu()
796 using namespace Menu_Helpers;
798 Menu* mode_menu = manage (new Menu);
799 MenuList& items = mode_menu->items();
800 mode_menu->set_name ("ArdourContextMenu");
802 RadioMenuItem::Group mode_group;
803 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
804 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
805 MeterColors, false, true, true)));
806 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
807 _meter_color_mode_item->set_active(_color_mode == MeterColors);
809 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
810 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
811 ChannelColors, false, true, true)));
812 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
813 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
815 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
816 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
817 TrackColor, false, true, true)));
818 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
819 _channel_color_mode_item->set_active(_color_mode == TrackColor);
825 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
827 if (apply_to_selection) {
828 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
830 if (_note_mode != mode || midi_track()->note_mode() != mode) {
832 midi_track()->set_note_mode(mode);
833 set_gui_property ("note-mode", enum_2_string(_note_mode));
834 _view->redisplay_track();
840 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
842 if (apply_to_selection) {
843 _editor.get_selection().tracks.foreach_midi_time_axis (
844 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false)
848 if (_color_mode == mode && !force) {
852 if (mode == ChannelColors) {
853 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
855 _channel_selector.set_default_channel_color();
859 set_gui_property ("color-mode", enum_2_string(_color_mode));
861 _view->redisplay_track();
867 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
869 if (apply_to_selection) {
870 _editor.get_selection().tracks.foreach_midi_time_axis (
871 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false)
874 if (!_ignore_signals) {
875 midi_view()->set_note_range(range);
881 MidiTimeAxisView::update_range()
883 MidiGhostRegion* mgr;
885 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
886 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
893 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
895 if (apply_to_selection) {
896 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
899 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
901 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
902 create_automation_child(*i, true);
906 RouteTimeAxisView::show_all_automation ();
911 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
913 if (apply_to_selection) {
914 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
917 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
919 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
920 create_automation_child (*i, true);
924 RouteTimeAxisView::show_existing_automation ();
928 /** Create an automation track for the given parameter (pitch bend, channel pressure).
931 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
933 if (param.type() == NullAutomation) {
937 AutomationTracks::iterator existing = _automation_tracks.find (param);
939 if (existing != _automation_tracks.end()) {
941 /* automation track created because we had existing data for
942 * the processor, but visibility may need to be controlled
943 * since it will have been set visible by default.
946 if (existing->second->set_marked_for_display (show) && !no_redraw) {
953 boost::shared_ptr<AutomationTimeAxisView> track;
955 switch (param.type()) {
958 create_gain_automation_child (param, show);
961 case PluginAutomation:
962 /* handled elsewhere */
965 case MidiCCAutomation:
966 case MidiPgmChangeAutomation:
967 case MidiPitchBenderAutomation:
968 case MidiChannelPressureAutomation:
969 case MidiSystemExclusiveAutomation:
970 /* These controllers are region "automation" - they are owned
971 * by regions (and their MidiModels), not by the track. As a
972 * result we do not create an AutomationList/Line for the track
973 * ... except here we are doing something!! XXX
976 track.reset (new AutomationTimeAxisView (
979 boost::shared_ptr<Automatable> (),
980 boost::shared_ptr<AutomationControl> (),
986 _route->describe_parameter(param)
990 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
993 add_automation_child (param, track, show);
997 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
1002 MidiTimeAxisView::route_active_changed ()
1004 RouteUI::route_active_changed ();
1007 if (_route->active()) {
1008 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1009 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1010 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1012 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1013 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1014 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1020 if (_route->active()) {
1021 controls_ebox.set_name ("BusControlsBaseUnselected");
1022 controls_base_selected_name = "BusControlsBaseSelected";
1023 controls_base_unselected_name = "BusControlsBaseUnselected";
1025 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1026 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1027 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1033 MidiTimeAxisView::set_note_selection (uint8_t note)
1035 if (!_editor.internal_editing()) {
1039 uint16_t chn_mask = _channel_selector.get_selected_channels();
1041 if (_view->num_selected_regionviews() == 0) {
1042 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1044 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1049 MidiTimeAxisView::add_note_selection (uint8_t note)
1051 if (!_editor.internal_editing()) {
1055 uint16_t chn_mask = _channel_selector.get_selected_channels();
1057 if (_view->num_selected_regionviews() == 0) {
1058 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1060 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1065 MidiTimeAxisView::extend_note_selection (uint8_t note)
1067 if (!_editor.internal_editing()) {
1071 uint16_t chn_mask = _channel_selector.get_selected_channels();
1073 if (_view->num_selected_regionviews() == 0) {
1074 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1076 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1081 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1083 if (!_editor.internal_editing()) {
1087 uint16_t chn_mask = _channel_selector.get_selected_channels();
1089 if (_view->num_selected_regionviews() == 0) {
1090 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1092 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1097 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1099 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1103 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1105 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1109 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1111 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1115 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1117 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1121 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1123 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1127 uint16_t selected_channels = _channel_selector.get_selected_channels();
1128 bool changed = false;
1132 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1134 for (uint32_t chn = 0; chn < 16; ++chn) {
1135 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1136 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1142 if ((selected_channels & (0x0001 << chn)) == 0) {
1143 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1144 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1146 changed = track->set_marked_for_display (false) || changed;
1148 changed = track->set_marked_for_display (true) || changed;
1155 /* TODO: Bender, Pressure */
1157 /* invalidate the controller menu, so that we rebuild it next time */
1158 _controller_menu_map.clear ();
1159 delete controller_menu;
1160 controller_menu = 0;
1168 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1170 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1175 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1176 if (i != _controller_menu_map.end()) {
1180 i = _channel_command_menu_map.find (param);
1181 if (i != _channel_command_menu_map.end()) {
1188 boost::shared_ptr<MidiRegion>
1189 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1191 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1193 real_editor->begin_reversible_command (Operations::create_region);
1194 playlist()->clear_changes ();
1196 real_editor->snap_to (pos, 0);
1198 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1199 view()->trackview().track()->name());
1202 plist.add (ARDOUR::Properties::start, 0);
1203 plist.add (ARDOUR::Properties::length, length);
1204 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1206 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1208 playlist()->add_region (region, pos);
1209 _session->add_command (new StatefulDiffCommand (playlist()));
1212 real_editor->commit_reversible_command ();
1215 return boost::dynamic_pointer_cast<MidiRegion>(region);
1219 MidiTimeAxisView::ensure_step_editor ()
1221 if (!_step_editor) {
1222 _step_editor = new StepEditor (_editor, midi_track(), *this);
1227 MidiTimeAxisView::start_step_editing ()
1229 ensure_step_editor ();
1230 _step_editor->start_step_editing ();
1234 MidiTimeAxisView::stop_step_editing ()
1237 _step_editor->stop_step_editing ();
1242 /** @return channel (counted from 0) to add an event to, based on the current setting
1243 * of the channel selector.
1246 MidiTimeAxisView::get_channel_for_add () const
1248 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1250 uint8_t channel = 0;
1252 /* pick the highest selected channel, unless all channels are selected,
1253 which is interpreted to mean channel 1 (zero)
1256 for (uint16_t i = 0; i < 16; ++i) {
1257 if (chn_mask & (1<<i)) {
1263 if (chn_cnt == 16) {
1271 MidiTimeAxisView::note_range_changed ()
1273 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1274 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1278 MidiTimeAxisView::contents_height_changed ()
1280 _range_scroomer->set_size_request (-1, _view->child_height ());