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/file_source.h"
42 #include "ardour/ladspa_plugin.h"
43 #include "ardour/location.h"
44 #include "ardour/midi_diskstream.h"
45 #include "ardour/midi_patch_manager.h"
46 #include "ardour/midi_playlist.h"
47 #include "ardour/midi_region.h"
48 #include "ardour/midi_source.h"
49 #include "ardour/operations.h"
50 #include "ardour/playlist.h"
51 #include "ardour/processor.h"
52 #include "ardour/region_factory.h"
53 #include "ardour/session.h"
54 #include "ardour/session_playlist.h"
55 #include "ardour/tempo.h"
56 #include "ardour/utils.h"
58 #include "midi++/names.h"
60 #include "add_midi_cc_track_dialog.h"
61 #include "ardour_ui.h"
62 #include "ardour_button.h"
63 #include "automation_line.h"
64 #include "automation_time_axis.h"
65 #include "canvas-note-event.h"
66 #include "canvas_impl.h"
67 #include "crossfade_view.h"
70 #include "ghostregion.h"
71 #include "gui_thread.h"
73 #include "midi_scroomer.h"
74 #include "midi_streamview.h"
75 #include "midi_region_view.h"
76 #include "midi_time_axis.h"
77 #include "piano_roll_header.h"
78 #include "playlist_selector.h"
79 #include "plugin_selector.h"
80 #include "plugin_ui.h"
81 #include "point_selection.h"
83 #include "region_view.h"
84 #include "rgb_macros.h"
85 #include "selection.h"
86 #include "step_editor.h"
87 #include "simplerect.h"
90 #include "ardour/midi_track.h"
94 using namespace ARDOUR;
97 using namespace Gtkmm2ext;
98 using namespace Editing;
100 // Minimum height at which a control is displayed
101 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
102 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
104 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
105 : AxisView(sess) // virtually inherited
106 , RouteTimeAxisView(ed, sess, canvas)
107 , _ignore_signals(false)
109 , _piano_roll_header(0)
110 , _note_mode(Sustained)
112 , _percussion_mode_item(0)
113 , _color_mode(MeterColors)
114 , _meter_color_mode_item(0)
115 , _channel_color_mode_item(0)
116 , _track_color_mode_item(0)
117 , _step_edit_item (0)
118 , _midi_thru_item (0)
119 , controller_menu (0)
125 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
127 RouteTimeAxisView::set_route (rt);
129 subplugin_menu.set_name ("ArdourContextMenu");
131 _view = new MidiStreamView (*this);
132 if (!gui_property ("note-range-min").empty ()) {
133 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
135 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
137 ignore_toggle = false;
139 mute_button->unset_active_state ();
140 solo_button->unset_active_state ();
142 if (is_midi_track()) {
143 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
144 _note_mode = midi_track()->note_mode();
145 } else { // MIDI bus (which doesn't exist yet..)
146 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
149 /* map current state of the route */
151 processors_changed (RouteProcessorChange ());
153 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
156 _piano_roll_header = new PianoRollHeader(*midi_view());
158 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
159 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
160 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
162 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
164 /* Suspend updates of the StreamView during scroomer drags to speed things up */
165 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
166 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
168 controls_hbox.pack_start(*_range_scroomer);
169 controls_hbox.pack_start(*_piano_roll_header);
171 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
172 controls_base_selected_name = "MidiTrackControlsBaseSelected";
173 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
175 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
177 /* ask for notifications of any new RegionViews */
178 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
180 if (!_editor.have_idled()) {
181 /* first idle will do what we need */
187 HBox* midi_controls_hbox = manage(new HBox());
189 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
191 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
192 for (; m != patch_manager.all_models().end(); ++m) {
193 _model_selector.append_text(m->c_str());
196 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
198 _custom_device_mode_selector.signal_changed().connect(
199 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
201 // TODO: persist the choice
202 // this initializes the comboboxes and sends out the signal
203 _model_selector.set_active(0);
205 midi_controls_hbox->pack_start(_channel_selector, true, false);
206 if (!patch_manager.all_models().empty()) {
207 _midi_controls_box.pack_start(_model_selector, true, false);
208 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
211 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
213 controls_vbox.pack_start(_midi_controls_box, false, false);
215 // restore channel selector settings
216 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
217 _channel_selector.mode_changed.connect(
218 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
219 _channel_selector.mode_changed.connect(
220 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
222 string prop = gui_property ("color-mode");
224 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
225 if (_color_mode == ChannelColors) {
226 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
230 set_color_mode (_color_mode, true, false);
232 prop = gui_property ("note-mode");
234 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
235 if (_percussion_mode_item) {
236 _percussion_mode_item->set_active (_note_mode == Percussive);
240 /* Look for any GUI object state nodes that represent automation children that should exist, and create
244 GUIObjectState& gui_state = gui_object_state ();
245 for (GUIObjectState::StringPropertyMap::const_iterator i = gui_state.begin(); i != gui_state.end(); ++i) {
248 Evoral::Parameter parameter (0, 0, 0);
250 bool const p = AutomationTimeAxisView::parse_state_id (i->first, route_id, has_parameter, parameter);
251 if (p && route_id == _route->id () && has_parameter) {
252 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (i->first, X_("visible"))));
258 MidiTimeAxisView::first_idle ()
265 MidiTimeAxisView::~MidiTimeAxisView ()
267 delete _piano_roll_header;
268 _piano_roll_header = 0;
270 delete _range_scroomer;
273 delete controller_menu;
278 MidiTimeAxisView::enter_internal_edit_mode ()
281 midi_view()->enter_internal_edit_mode ();
286 MidiTimeAxisView::leave_internal_edit_mode ()
289 midi_view()->leave_internal_edit_mode ();
294 MidiTimeAxisView::check_step_edit ()
296 ensure_step_editor ();
297 _step_editor->check_step_edit ();
301 MidiTimeAxisView::model_changed()
303 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
304 .custom_device_mode_names_by_model(_model_selector.get_active_text());
306 _custom_device_mode_selector.clear_items();
308 for (std::list<std::string>::const_iterator i = device_modes.begin();
309 i != device_modes.end(); ++i) {
310 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
311 _custom_device_mode_selector.append_text(*i);
314 _custom_device_mode_selector.set_active(0);
317 void MidiTimeAxisView::custom_device_mode_changed()
319 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
320 _custom_device_mode_selector.get_active_text());
324 MidiTimeAxisView::midi_view()
326 return dynamic_cast<MidiStreamView*>(_view);
330 MidiTimeAxisView::set_height (uint32_t h)
332 RouteTimeAxisView::set_height (h);
334 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
335 _midi_controls_box.show_all ();
337 _midi_controls_box.hide();
340 if (height >= KEYBOARD_MIN_HEIGHT) {
341 if (is_track() && _range_scroomer) {
342 _range_scroomer->show();
344 if (is_track() && _piano_roll_header) {
345 _piano_roll_header->show();
348 if (is_track() && _range_scroomer) {
349 _range_scroomer->hide();
351 if (is_track() && _piano_roll_header) {
352 _piano_roll_header->hide();
358 MidiTimeAxisView::append_extra_display_menu_items ()
360 using namespace Menu_Helpers;
362 MenuList& items = display_menu->items();
365 Menu *range_menu = manage(new Menu);
366 MenuList& range_items = range_menu->items();
367 range_menu->set_name ("ArdourContextMenu");
369 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
370 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
371 MidiStreamView::FullRange)));
373 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
374 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
375 MidiStreamView::ContentsRange)));
377 items.push_back (MenuElem (_("Note Range"), *range_menu));
378 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
380 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
381 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
383 items.push_back (SeparatorElem ());
387 MidiTimeAxisView::toggle_midi_thru ()
389 if (!_midi_thru_item) {
393 bool view_yn = _midi_thru_item->get_active();
394 if (view_yn != midi_track()->midi_thru()) {
395 midi_track()->set_midi_thru (view_yn);
400 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
402 using namespace Menu_Helpers;
404 /* If we have a controller menu, we need to detach it before
405 RouteTimeAxis::build_automation_action_menu destroys the
406 menu it is attached to. Otherwise GTK destroys
407 controller_menu's gobj, meaning that it can't be reattached
408 below. See bug #3134.
411 if (controller_menu) {
412 detach_menu (*controller_menu);
415 _channel_command_menu_map.clear ();
416 RouteTimeAxisView::build_automation_action_menu (for_selection);
418 MenuList& automation_items = automation_action_menu->items();
420 uint16_t selected_channels = _channel_selector.get_selected_channels();
422 if (selected_channels != 0) {
424 automation_items.push_back (SeparatorElem());
426 /* these 2 MIDI "command" types are semantically more like automation than note data,
427 but they are not MIDI controllers. We give them special status in this menu, since
428 they will not show up in the controller list and anyone who actually knows
429 something about MIDI (!) would not expect to find them there.
432 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
433 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
434 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
435 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
437 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
438 since it might need to be updated after a channel mode change or other change. Also detach it
439 first in case it has been used anywhere else.
442 build_controller_menu ();
444 automation_items.push_back (SeparatorElem());
445 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
446 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
448 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
449 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
455 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
457 uint16_t selected_channels = _channel_selector.get_selected_channels();
459 for (uint8_t chn = 0; chn < 16; chn++) {
460 if (selected_channels & (0x0001 << chn)) {
462 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
463 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
466 menu->set_active (yn);
473 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
475 using namespace Menu_Helpers;
477 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
480 uint16_t selected_channels = _channel_selector.get_selected_channels();
483 for (uint8_t chn = 0; chn < 16; chn++) {
484 if (selected_channels & (0x0001 << chn)) {
493 /* multiple channels - create a submenu, with 1 item per channel */
495 Menu* chn_menu = manage (new Menu);
496 MenuList& chn_items (chn_menu->items());
497 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
499 /* add a couple of items to hide/show all of them */
501 chn_items.push_back (MenuElem (_("Hide all channels"),
502 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
503 false, param_without_channel)));
504 chn_items.push_back (MenuElem (_("Show all channels"),
505 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
506 true, param_without_channel)));
508 for (uint8_t chn = 0; chn < 16; chn++) {
509 if (selected_channels & (0x0001 << chn)) {
511 /* for each selected channel, add a menu item for this controller */
513 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
514 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
515 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
516 fully_qualified_param)));
518 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
519 bool visible = false;
522 if (track->marked_for_display()) {
527 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
528 _channel_command_menu_map[fully_qualified_param] = cmi;
529 cmi->set_active (visible);
533 /* now create an item in the parent menu that has the per-channel list as a submenu */
535 items.push_back (MenuElem (label, *chn_menu));
539 /* just one channel - create a single menu item for this command+channel combination*/
541 for (uint8_t chn = 0; chn < 16; chn++) {
542 if (selected_channels & (0x0001 << chn)) {
544 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
545 items.push_back (CheckMenuElem (label,
546 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
547 fully_qualified_param)));
549 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
550 bool visible = false;
553 if (track->marked_for_display()) {
558 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
559 _channel_command_menu_map[fully_qualified_param] = cmi;
560 cmi->set_active (visible);
562 /* one channel only */
570 MidiTimeAxisView::build_controller_menu ()
572 using namespace Menu_Helpers;
574 if (controller_menu) {
575 /* it exists and has not been invalidated by a channel mode change, so just return it */
579 controller_menu = new Menu; // explicitly managed by us
580 MenuList& items (controller_menu->items());
582 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
583 for each controller+channel combination covering the currently selected channels for this track
586 uint16_t selected_channels = _channel_selector.get_selected_channels();
588 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
593 for (uint8_t chn = 0; chn < 16; chn++) {
594 if (selected_channels & (0x0001 << chn)) {
601 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
602 bank select controllers, as they are handled by the `patch' code */
604 for (int i = 0; i < 127; i += 16) {
606 Menu* ctl_menu = manage (new Menu);
607 MenuList& ctl_items (ctl_menu->items());
610 /* for each controller, consider whether to create a submenu or a single item */
612 for (int ctl = i; ctl < i+16; ++ctl) {
614 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
620 /* multiple channels - create a submenu, with 1 item per channel */
622 Menu* chn_menu = manage (new Menu);
623 MenuList& chn_items (chn_menu->items());
625 /* add a couple of items to hide/show this controller on all channels */
627 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
628 chn_items.push_back (MenuElem (_("Hide all channels"),
629 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
630 false, param_without_channel)));
631 chn_items.push_back (MenuElem (_("Show all channels"),
632 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
633 true, param_without_channel)));
635 for (uint8_t chn = 0; chn < 16; chn++) {
636 if (selected_channels & (0x0001 << chn)) {
638 /* for each selected channel, add a menu item for this controller */
640 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
641 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
642 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
643 fully_qualified_param)));
645 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
646 bool visible = false;
649 if (track->marked_for_display()) {
654 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
655 _controller_menu_map[fully_qualified_param] = cmi;
656 cmi->set_active (visible);
660 /* add the per-channel menu to the list of controllers, with the name of the controller */
661 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
662 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
666 /* just one channel - create a single menu item for this ctl+channel combination*/
668 for (uint8_t chn = 0; chn < 16; chn++) {
669 if (selected_channels & (0x0001 << chn)) {
671 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
672 ctl_items.push_back (
674 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
675 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
676 fully_qualified_param)
679 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
681 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
682 bool visible = false;
685 if (track->marked_for_display()) {
690 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
691 _controller_menu_map[fully_qualified_param] = cmi;
692 cmi->set_active (visible);
694 /* one channel only */
701 /* add the menu for this block of controllers to the overall controller menu */
703 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
708 MidiTimeAxisView::build_note_mode_menu()
710 using namespace Menu_Helpers;
712 Menu* mode_menu = manage (new Menu);
713 MenuList& items = mode_menu->items();
714 mode_menu->set_name ("ArdourContextMenu");
716 RadioMenuItem::Group mode_group;
717 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
718 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
719 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
720 _note_mode_item->set_active(_note_mode == Sustained);
722 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
723 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
724 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
725 _percussion_mode_item->set_active(_note_mode == Percussive);
731 MidiTimeAxisView::build_color_mode_menu()
733 using namespace Menu_Helpers;
735 Menu* mode_menu = manage (new Menu);
736 MenuList& items = mode_menu->items();
737 mode_menu->set_name ("ArdourContextMenu");
739 RadioMenuItem::Group mode_group;
740 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
741 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
742 MeterColors, false, true)));
743 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
744 _meter_color_mode_item->set_active(_color_mode == MeterColors);
746 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
747 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
748 ChannelColors, false, true)));
749 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
750 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
752 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
753 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
754 TrackColor, false, true)));
755 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
756 _channel_color_mode_item->set_active(_color_mode == TrackColor);
762 MidiTimeAxisView::set_note_mode(NoteMode mode)
764 if (_note_mode != mode || midi_track()->note_mode() != mode) {
766 midi_track()->set_note_mode(mode);
767 set_gui_property ("note-mode", enum_2_string(_note_mode));
768 _view->redisplay_track();
773 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
775 if (_color_mode == mode && !force) {
779 if (mode == ChannelColors) {
780 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
782 _channel_selector.set_default_channel_color();
786 set_gui_property ("color-mode", enum_2_string(_color_mode));
788 _view->redisplay_track();
793 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
795 if (!_ignore_signals)
796 midi_view()->set_note_range(range);
801 MidiTimeAxisView::update_range()
803 MidiGhostRegion* mgr;
805 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
806 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
813 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
815 if (apply_to_selection) {
816 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
819 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
821 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
822 create_automation_child(*i, true);
826 RouteTimeAxisView::show_all_automation ();
831 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
833 if (apply_to_selection) {
834 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
837 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
839 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
840 create_automation_child (*i, true);
844 RouteTimeAxisView::show_existing_automation ();
848 /** Create an automation track for the given parameter (pitch bend, channel pressure).
851 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
853 if (param.type() == NullAutomation) {
854 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
858 AutomationTracks::iterator existing = _automation_tracks.find (param);
860 if (existing != _automation_tracks.end()) {
862 /* automation track created because we had existing data for
863 * the processor, but visibility may need to be controlled
864 * since it will have been set visible by default.
867 cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
869 if (existing->second->set_marked_for_display (show) && !no_redraw) {
876 boost::shared_ptr<AutomationTimeAxisView> track;
878 switch (param.type()) {
881 create_gain_automation_child (param, show);
884 case PluginAutomation:
885 /* handled elsewhere */
888 case MidiCCAutomation:
889 case MidiPgmChangeAutomation:
890 case MidiPitchBenderAutomation:
891 case MidiChannelPressureAutomation:
892 case MidiSystemExclusiveAutomation:
893 /* These controllers are region "automation" - they are owned
894 * by regions (and their MidiModels), not by the track. As a
895 * result we do not create an AutomationList/Line for the track
896 * ... except here we are doing something!! XXX
899 track.reset (new AutomationTimeAxisView (
902 boost::shared_ptr<Automatable> (),
903 boost::shared_ptr<AutomationControl> (),
909 _route->describe_parameter(param)
913 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
916 add_automation_child (param, track, show);
920 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
925 MidiTimeAxisView::route_active_changed ()
927 RouteUI::route_active_changed ();
930 if (_route->active()) {
931 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
932 controls_base_selected_name = "MidiTrackControlsBaseSelected";
933 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
935 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
936 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
937 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
943 if (_route->active()) {
944 controls_ebox.set_name ("BusControlsBaseUnselected");
945 controls_base_selected_name = "BusControlsBaseSelected";
946 controls_base_unselected_name = "BusControlsBaseUnselected";
948 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
949 controls_base_selected_name = "BusControlsBaseInactiveSelected";
950 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
958 MidiTimeAxisView::add_note_selection (uint8_t note)
960 if (!_editor.internal_editing()) {
964 uint16_t chn_mask = _channel_selector.get_selected_channels();
966 if (_view->num_selected_regionviews() == 0) {
967 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
969 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
974 MidiTimeAxisView::extend_note_selection (uint8_t note)
976 if (!_editor.internal_editing()) {
980 uint16_t chn_mask = _channel_selector.get_selected_channels();
982 if (_view->num_selected_regionviews() == 0) {
983 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
985 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
990 MidiTimeAxisView::toggle_note_selection (uint8_t note)
992 if (!_editor.internal_editing()) {
996 uint16_t chn_mask = _channel_selector.get_selected_channels();
998 if (_view->num_selected_regionviews() == 0) {
999 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1001 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1006 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1008 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1012 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1014 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1018 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1020 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1024 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1026 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1030 uint16_t selected_channels = _channel_selector.get_selected_channels();
1031 bool changed = false;
1035 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1037 for (uint32_t chn = 0; chn < 16; ++chn) {
1038 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1039 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1045 if ((selected_channels & (0x0001 << chn)) == 0) {
1046 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1047 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1049 changed = track->set_marked_for_display (false) || changed;
1051 changed = track->set_marked_for_display (true) || changed;
1058 /* TODO: Bender, Pressure */
1060 /* invalidate the controller menu, so that we rebuild it next time */
1061 _controller_menu_map.clear ();
1062 delete controller_menu;
1063 controller_menu = 0;
1071 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1073 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1078 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1079 if (i != _controller_menu_map.end()) {
1083 i = _channel_command_menu_map.find (param);
1084 if (i != _channel_command_menu_map.end()) {
1091 boost::shared_ptr<MidiRegion>
1092 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1094 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1096 real_editor->begin_reversible_command (Operations::create_region);
1097 playlist()->clear_changes ();
1099 real_editor->snap_to (pos, 0);
1101 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1102 view()->trackview().track()->name());
1105 plist.add (ARDOUR::Properties::start, 0);
1106 plist.add (ARDOUR::Properties::length, length);
1107 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1109 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1111 playlist()->add_region (region, pos);
1112 _session->add_command (new StatefulDiffCommand (playlist()));
1115 real_editor->commit_reversible_command ();
1118 return boost::dynamic_pointer_cast<MidiRegion>(region);
1122 MidiTimeAxisView::ensure_step_editor ()
1124 if (!_step_editor) {
1125 _step_editor = new StepEditor (_editor, midi_track(), *this);
1130 MidiTimeAxisView::start_step_editing ()
1132 ensure_step_editor ();
1133 _step_editor->start_step_editing ();
1137 MidiTimeAxisView::stop_step_editing ()
1140 _step_editor->stop_step_editing ();
1145 /** @return channel (counted from 0) to add an event to, based on the current setting
1146 * of the channel selector.
1149 MidiTimeAxisView::get_channel_for_add () const
1151 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1153 uint8_t channel = 0;
1155 /* pick the highest selected channel, unless all channels are selected,
1156 which is interpreted to mean channel 1 (zero)
1159 for (uint16_t i = 0; i < 16; ++i) {
1160 if (chn_mask & (1<<i)) {
1166 if (chn_cnt == 16) {
1174 MidiTimeAxisView::note_range_changed ()
1176 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1177 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());