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)
129 _view = new MidiStreamView (*this);
132 _piano_roll_header = new PianoRollHeader(*midi_view());
133 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
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 subplugin_menu.set_name ("ArdourContextMenu");
144 if (!gui_property ("note-range-min").empty ()) {
145 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
147 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
149 ignore_toggle = false;
151 if (is_midi_track()) {
152 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
153 _note_mode = midi_track()->note_mode();
154 } else { // MIDI bus (which doesn't exist yet..)
155 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
158 /* map current state of the route */
160 processors_changed (RouteProcessorChange ());
162 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
165 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
166 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
167 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
169 /* Suspend updates of the StreamView during scroomer drags to speed things up */
170 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
171 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
173 controls_hbox.pack_start(*_range_scroomer);
174 controls_hbox.pack_start(*_piano_roll_header);
176 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
177 controls_base_selected_name = "MidiTrackControlsBaseSelected";
178 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
180 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
182 /* ask for notifications of any new RegionViews */
183 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
185 if (!_editor.have_idled()) {
186 /* first idle will do what we need */
192 HBox* midi_controls_hbox = manage(new HBox());
194 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
196 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
197 for (; m != patch_manager.all_models().end(); ++m) {
198 _model_selector.append_text(m->c_str());
201 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
203 _custom_device_mode_selector.signal_changed().connect(
204 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
206 // TODO: persist the choice
207 // this initializes the comboboxes and sends out the signal
208 _model_selector.set_active(0);
210 midi_controls_hbox->pack_start(_channel_selector, true, false);
211 if (!patch_manager.all_models().empty()) {
212 _midi_controls_box.pack_start(_model_selector, true, false);
213 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
216 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
218 controls_vbox.pack_start(_midi_controls_box, false, false);
220 // restore channel selector settings
221 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
222 _channel_selector.mode_changed.connect(
223 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
224 _channel_selector.mode_changed.connect(
225 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
227 string prop = gui_property ("color-mode");
229 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
230 if (_color_mode == ChannelColors) {
231 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
235 set_color_mode (_color_mode, true, false);
237 prop = gui_property ("note-mode");
239 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
240 if (_percussion_mode_item) {
241 _percussion_mode_item->set_active (_note_mode == Percussive);
245 /* Look for any GUI object state nodes that represent automation children that should exist, and create
249 GUIObjectState& gui_state = gui_object_state ();
250 for (GUIObjectState::StringPropertyMap::const_iterator i = gui_state.begin(); i != gui_state.end(); ++i) {
253 Evoral::Parameter parameter (0, 0, 0);
255 bool const p = AutomationTimeAxisView::parse_state_id (i->first, route_id, has_parameter, parameter);
256 if (p && route_id == _route->id () && has_parameter) {
257 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (i->first, X_("visible"))));
263 MidiTimeAxisView::first_idle ()
270 MidiTimeAxisView::~MidiTimeAxisView ()
272 delete _piano_roll_header;
273 _piano_roll_header = 0;
275 delete _range_scroomer;
278 delete controller_menu;
283 MidiTimeAxisView::enter_internal_edit_mode ()
286 midi_view()->enter_internal_edit_mode ();
291 MidiTimeAxisView::leave_internal_edit_mode ()
294 midi_view()->leave_internal_edit_mode ();
299 MidiTimeAxisView::check_step_edit ()
301 ensure_step_editor ();
302 _step_editor->check_step_edit ();
306 MidiTimeAxisView::model_changed()
308 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
309 .custom_device_mode_names_by_model(_model_selector.get_active_text());
311 _custom_device_mode_selector.clear_items();
313 for (std::list<std::string>::const_iterator i = device_modes.begin();
314 i != device_modes.end(); ++i) {
315 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
316 _custom_device_mode_selector.append_text(*i);
319 _custom_device_mode_selector.set_active(0);
322 void MidiTimeAxisView::custom_device_mode_changed()
324 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
325 _custom_device_mode_selector.get_active_text());
329 MidiTimeAxisView::midi_view()
331 return dynamic_cast<MidiStreamView*>(_view);
335 MidiTimeAxisView::set_height (uint32_t h)
337 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
338 _midi_controls_box.show_all ();
340 _midi_controls_box.hide();
343 if (h >= KEYBOARD_MIN_HEIGHT) {
344 if (is_track() && _range_scroomer) {
345 _range_scroomer->show();
347 if (is_track() && _piano_roll_header) {
348 _piano_roll_header->show();
351 if (is_track() && _range_scroomer) {
352 _range_scroomer->hide();
354 if (is_track() && _piano_roll_header) {
355 _piano_roll_header->hide();
359 /* We need to do this after changing visibility of our stuff, as it will
360 eventually trigger a call to Editor::reset_controls_layout_width(),
361 which needs to know if we have just shown or hidden a scroomer /
364 RouteTimeAxisView::set_height (h);
368 MidiTimeAxisView::append_extra_display_menu_items ()
370 using namespace Menu_Helpers;
372 MenuList& items = display_menu->items();
375 Menu *range_menu = manage(new Menu);
376 MenuList& range_items = range_menu->items();
377 range_menu->set_name ("ArdourContextMenu");
379 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
380 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
381 MidiStreamView::FullRange)));
383 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
384 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
385 MidiStreamView::ContentsRange)));
387 items.push_back (MenuElem (_("Note Range"), *range_menu));
388 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
390 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
391 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
393 items.push_back (SeparatorElem ());
397 MidiTimeAxisView::toggle_midi_thru ()
399 if (!_midi_thru_item) {
403 bool view_yn = _midi_thru_item->get_active();
404 if (view_yn != midi_track()->midi_thru()) {
405 midi_track()->set_midi_thru (view_yn);
410 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
412 using namespace Menu_Helpers;
414 /* If we have a controller menu, we need to detach it before
415 RouteTimeAxis::build_automation_action_menu destroys the
416 menu it is attached to. Otherwise GTK destroys
417 controller_menu's gobj, meaning that it can't be reattached
418 below. See bug #3134.
421 if (controller_menu) {
422 detach_menu (*controller_menu);
425 _channel_command_menu_map.clear ();
426 RouteTimeAxisView::build_automation_action_menu (for_selection);
428 MenuList& automation_items = automation_action_menu->items();
430 uint16_t selected_channels = _channel_selector.get_selected_channels();
432 if (selected_channels != 0) {
434 automation_items.push_back (SeparatorElem());
436 /* these 2 MIDI "command" types are semantically more like automation than note data,
437 but they are not MIDI controllers. We give them special status in this menu, since
438 they will not show up in the controller list and anyone who actually knows
439 something about MIDI (!) would not expect to find them there.
442 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
443 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
444 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
445 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
447 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
448 since it might need to be updated after a channel mode change or other change. Also detach it
449 first in case it has been used anywhere else.
452 build_controller_menu ();
454 automation_items.push_back (SeparatorElem());
455 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
456 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
458 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
459 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
465 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
467 uint16_t selected_channels = _channel_selector.get_selected_channels();
469 for (uint8_t chn = 0; chn < 16; chn++) {
470 if (selected_channels & (0x0001 << chn)) {
472 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
473 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
476 menu->set_active (yn);
483 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
485 using namespace Menu_Helpers;
487 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
490 uint16_t selected_channels = _channel_selector.get_selected_channels();
493 for (uint8_t chn = 0; chn < 16; chn++) {
494 if (selected_channels & (0x0001 << chn)) {
503 /* multiple channels - create a submenu, with 1 item per channel */
505 Menu* chn_menu = manage (new Menu);
506 MenuList& chn_items (chn_menu->items());
507 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
509 /* add a couple of items to hide/show all of them */
511 chn_items.push_back (MenuElem (_("Hide all channels"),
512 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
513 false, param_without_channel)));
514 chn_items.push_back (MenuElem (_("Show all channels"),
515 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
516 true, param_without_channel)));
518 for (uint8_t chn = 0; chn < 16; chn++) {
519 if (selected_channels & (0x0001 << chn)) {
521 /* for each selected channel, add a menu item for this controller */
523 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
524 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
525 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
526 fully_qualified_param)));
528 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
529 bool visible = false;
532 if (track->marked_for_display()) {
537 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
538 _channel_command_menu_map[fully_qualified_param] = cmi;
539 cmi->set_active (visible);
543 /* now create an item in the parent menu that has the per-channel list as a submenu */
545 items.push_back (MenuElem (label, *chn_menu));
549 /* just one channel - create a single menu item for this command+channel combination*/
551 for (uint8_t chn = 0; chn < 16; chn++) {
552 if (selected_channels & (0x0001 << chn)) {
554 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
555 items.push_back (CheckMenuElem (label,
556 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
557 fully_qualified_param)));
559 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
560 bool visible = false;
563 if (track->marked_for_display()) {
568 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
569 _channel_command_menu_map[fully_qualified_param] = cmi;
570 cmi->set_active (visible);
572 /* one channel only */
580 MidiTimeAxisView::build_controller_menu ()
582 using namespace Menu_Helpers;
584 if (controller_menu) {
585 /* it exists and has not been invalidated by a channel mode change, so just return it */
589 controller_menu = new Menu; // explicitly managed by us
590 MenuList& items (controller_menu->items());
592 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
593 for each controller+channel combination covering the currently selected channels for this track
596 uint16_t selected_channels = _channel_selector.get_selected_channels();
598 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
603 for (uint8_t chn = 0; chn < 16; chn++) {
604 if (selected_channels & (0x0001 << chn)) {
611 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
612 bank select controllers, as they are handled by the `patch' code */
614 for (int i = 0; i < 127; i += 16) {
616 Menu* ctl_menu = manage (new Menu);
617 MenuList& ctl_items (ctl_menu->items());
620 /* for each controller, consider whether to create a submenu or a single item */
622 for (int ctl = i; ctl < i+16; ++ctl) {
624 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
630 /* multiple channels - create a submenu, with 1 item per channel */
632 Menu* chn_menu = manage (new Menu);
633 MenuList& chn_items (chn_menu->items());
635 /* add a couple of items to hide/show this controller on all channels */
637 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
638 chn_items.push_back (MenuElem (_("Hide all channels"),
639 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
640 false, param_without_channel)));
641 chn_items.push_back (MenuElem (_("Show all channels"),
642 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
643 true, param_without_channel)));
645 for (uint8_t chn = 0; chn < 16; chn++) {
646 if (selected_channels & (0x0001 << chn)) {
648 /* for each selected channel, add a menu item for this controller */
650 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
651 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
652 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
653 fully_qualified_param)));
655 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
656 bool visible = false;
659 if (track->marked_for_display()) {
664 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
665 _controller_menu_map[fully_qualified_param] = cmi;
666 cmi->set_active (visible);
670 /* add the per-channel menu to the list of controllers, with the name of the controller */
671 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
672 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
676 /* just one channel - create a single menu item for this ctl+channel combination*/
678 for (uint8_t chn = 0; chn < 16; chn++) {
679 if (selected_channels & (0x0001 << chn)) {
681 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
682 ctl_items.push_back (
684 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
685 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
686 fully_qualified_param)
689 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
691 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
692 bool visible = false;
695 if (track->marked_for_display()) {
700 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
701 _controller_menu_map[fully_qualified_param] = cmi;
702 cmi->set_active (visible);
704 /* one channel only */
711 /* add the menu for this block of controllers to the overall controller menu */
713 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
718 MidiTimeAxisView::build_note_mode_menu()
720 using namespace Menu_Helpers;
722 Menu* mode_menu = manage (new Menu);
723 MenuList& items = mode_menu->items();
724 mode_menu->set_name ("ArdourContextMenu");
726 RadioMenuItem::Group mode_group;
727 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
728 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
729 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
730 _note_mode_item->set_active(_note_mode == Sustained);
732 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
733 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
734 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
735 _percussion_mode_item->set_active(_note_mode == Percussive);
741 MidiTimeAxisView::build_color_mode_menu()
743 using namespace Menu_Helpers;
745 Menu* mode_menu = manage (new Menu);
746 MenuList& items = mode_menu->items();
747 mode_menu->set_name ("ArdourContextMenu");
749 RadioMenuItem::Group mode_group;
750 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
751 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
752 MeterColors, false, true)));
753 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
754 _meter_color_mode_item->set_active(_color_mode == MeterColors);
756 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
757 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
758 ChannelColors, false, true)));
759 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
760 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
762 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
763 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
764 TrackColor, false, true)));
765 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
766 _channel_color_mode_item->set_active(_color_mode == TrackColor);
772 MidiTimeAxisView::set_note_mode(NoteMode mode)
774 if (_note_mode != mode || midi_track()->note_mode() != mode) {
776 midi_track()->set_note_mode(mode);
777 set_gui_property ("note-mode", enum_2_string(_note_mode));
778 _view->redisplay_track();
783 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
785 if (_color_mode == mode && !force) {
789 if (mode == ChannelColors) {
790 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
792 _channel_selector.set_default_channel_color();
796 set_gui_property ("color-mode", enum_2_string(_color_mode));
798 _view->redisplay_track();
803 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
805 if (!_ignore_signals)
806 midi_view()->set_note_range(range);
811 MidiTimeAxisView::update_range()
813 MidiGhostRegion* mgr;
815 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
816 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
823 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
825 if (apply_to_selection) {
826 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
829 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
831 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
832 create_automation_child(*i, true);
836 RouteTimeAxisView::show_all_automation ();
841 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
843 if (apply_to_selection) {
844 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
847 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
849 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
850 create_automation_child (*i, true);
854 RouteTimeAxisView::show_existing_automation ();
858 /** Create an automation track for the given parameter (pitch bend, channel pressure).
861 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
863 if (param.type() == NullAutomation) {
864 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
868 AutomationTracks::iterator existing = _automation_tracks.find (param);
870 if (existing != _automation_tracks.end()) {
872 /* automation track created because we had existing data for
873 * the processor, but visibility may need to be controlled
874 * since it will have been set visible by default.
877 cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
879 if (existing->second->set_marked_for_display (show) && !no_redraw) {
886 boost::shared_ptr<AutomationTimeAxisView> track;
888 switch (param.type()) {
891 create_gain_automation_child (param, show);
894 case PluginAutomation:
895 /* handled elsewhere */
898 case MidiCCAutomation:
899 case MidiPgmChangeAutomation:
900 case MidiPitchBenderAutomation:
901 case MidiChannelPressureAutomation:
902 case MidiSystemExclusiveAutomation:
903 /* These controllers are region "automation" - they are owned
904 * by regions (and their MidiModels), not by the track. As a
905 * result we do not create an AutomationList/Line for the track
906 * ... except here we are doing something!! XXX
909 track.reset (new AutomationTimeAxisView (
912 boost::shared_ptr<Automatable> (),
913 boost::shared_ptr<AutomationControl> (),
919 _route->describe_parameter(param)
923 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
926 add_automation_child (param, track, show);
930 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
935 MidiTimeAxisView::route_active_changed ()
937 RouteUI::route_active_changed ();
940 if (_route->active()) {
941 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
942 controls_base_selected_name = "MidiTrackControlsBaseSelected";
943 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
945 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
946 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
947 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
953 if (_route->active()) {
954 controls_ebox.set_name ("BusControlsBaseUnselected");
955 controls_base_selected_name = "BusControlsBaseSelected";
956 controls_base_unselected_name = "BusControlsBaseUnselected";
958 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
959 controls_base_selected_name = "BusControlsBaseInactiveSelected";
960 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
968 MidiTimeAxisView::add_note_selection (uint8_t note)
970 if (!_editor.internal_editing()) {
974 uint16_t chn_mask = _channel_selector.get_selected_channels();
976 if (_view->num_selected_regionviews() == 0) {
977 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
979 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
984 MidiTimeAxisView::extend_note_selection (uint8_t note)
986 if (!_editor.internal_editing()) {
990 uint16_t chn_mask = _channel_selector.get_selected_channels();
992 if (_view->num_selected_regionviews() == 0) {
993 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
995 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1000 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1002 if (!_editor.internal_editing()) {
1006 uint16_t chn_mask = _channel_selector.get_selected_channels();
1008 if (_view->num_selected_regionviews() == 0) {
1009 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1011 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1016 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1018 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1022 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1024 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1028 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1030 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1034 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1036 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1040 uint16_t selected_channels = _channel_selector.get_selected_channels();
1041 bool changed = false;
1045 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1047 for (uint32_t chn = 0; chn < 16; ++chn) {
1048 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1049 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1055 if ((selected_channels & (0x0001 << chn)) == 0) {
1056 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1057 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1059 changed = track->set_marked_for_display (false) || changed;
1061 changed = track->set_marked_for_display (true) || changed;
1068 /* TODO: Bender, Pressure */
1070 /* invalidate the controller menu, so that we rebuild it next time */
1071 _controller_menu_map.clear ();
1072 delete controller_menu;
1073 controller_menu = 0;
1081 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1083 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1088 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1089 if (i != _controller_menu_map.end()) {
1093 i = _channel_command_menu_map.find (param);
1094 if (i != _channel_command_menu_map.end()) {
1101 boost::shared_ptr<MidiRegion>
1102 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1104 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1106 real_editor->begin_reversible_command (Operations::create_region);
1107 playlist()->clear_changes ();
1109 real_editor->snap_to (pos, 0);
1111 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1112 view()->trackview().track()->name());
1115 plist.add (ARDOUR::Properties::start, 0);
1116 plist.add (ARDOUR::Properties::length, length);
1117 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1119 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1121 playlist()->add_region (region, pos);
1122 _session->add_command (new StatefulDiffCommand (playlist()));
1125 real_editor->commit_reversible_command ();
1128 return boost::dynamic_pointer_cast<MidiRegion>(region);
1132 MidiTimeAxisView::ensure_step_editor ()
1134 if (!_step_editor) {
1135 _step_editor = new StepEditor (_editor, midi_track(), *this);
1140 MidiTimeAxisView::start_step_editing ()
1142 ensure_step_editor ();
1143 _step_editor->start_step_editing ();
1147 MidiTimeAxisView::stop_step_editing ()
1150 _step_editor->stop_step_editing ();
1155 /** @return channel (counted from 0) to add an event to, based on the current setting
1156 * of the channel selector.
1159 MidiTimeAxisView::get_channel_for_add () const
1161 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1163 uint8_t channel = 0;
1165 /* pick the highest selected channel, unless all channels are selected,
1166 which is interpreted to mean channel 1 (zero)
1169 for (uint16_t i = 0; i < 16; ++i) {
1170 if (chn_mask & (1<<i)) {
1176 if (chn_cnt == 16) {
1184 MidiTimeAxisView::note_range_changed ()
1186 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1187 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());