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 _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));
151 ignore_toggle = false;
153 if (is_midi_track()) {
154 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
155 _note_mode = midi_track()->note_mode();
156 } else { // MIDI bus (which doesn't exist yet..)
157 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
160 /* map current state of the route */
162 processors_changed (RouteProcessorChange ());
164 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
167 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
168 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
169 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
171 /* Suspend updates of the StreamView during scroomer drags to speed things up */
172 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
173 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
175 controls_hbox.pack_start(*_range_scroomer);
176 controls_hbox.pack_start(*_piano_roll_header);
178 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
179 controls_base_selected_name = "MidiTrackControlsBaseSelected";
180 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
182 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
184 /* ask for notifications of any new RegionViews */
185 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
187 if (!_editor.have_idled()) {
188 /* first idle will do what we need */
194 HBox* midi_controls_hbox = manage(new HBox());
196 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
198 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
199 for (; m != patch_manager.all_models().end(); ++m) {
200 _model_selector.append_text(m->c_str());
203 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
205 _custom_device_mode_selector.signal_changed().connect(
206 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
208 // TODO: persist the choice
209 // this initializes the comboboxes and sends out the signal
210 _model_selector.set_active(0);
212 midi_controls_hbox->pack_start(_channel_selector, true, false);
213 if (!patch_manager.all_models().empty()) {
214 _midi_controls_box.pack_start(_model_selector, true, false);
215 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
218 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
220 controls_vbox.pack_start(_midi_controls_box, false, false);
222 // restore channel selector settings
223 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
224 _channel_selector.mode_changed.connect(
225 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
226 _channel_selector.mode_changed.connect(
227 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
229 string prop = gui_property ("color-mode");
231 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
232 if (_color_mode == ChannelColors) {
233 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
237 set_color_mode (_color_mode, true, false);
239 prop = gui_property ("note-mode");
241 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
242 if (_percussion_mode_item) {
243 _percussion_mode_item->set_active (_note_mode == Percussive);
247 /* Look for any GUI object state nodes that represent automation children that should exist, and create
251 GUIObjectState& gui_state = gui_object_state ();
252 for (GUIObjectState::StringPropertyMap::const_iterator i = gui_state.begin(); i != gui_state.end(); ++i) {
255 Evoral::Parameter parameter (0, 0, 0);
257 bool const p = AutomationTimeAxisView::parse_state_id (i->first, route_id, has_parameter, parameter);
258 if (p && route_id == _route->id () && has_parameter) {
259 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (i->first, X_("visible"))));
265 MidiTimeAxisView::first_idle ()
272 MidiTimeAxisView::~MidiTimeAxisView ()
274 delete _piano_roll_header;
275 _piano_roll_header = 0;
277 delete _range_scroomer;
280 delete controller_menu;
285 MidiTimeAxisView::enter_internal_edit_mode ()
288 midi_view()->enter_internal_edit_mode ();
293 MidiTimeAxisView::leave_internal_edit_mode ()
296 midi_view()->leave_internal_edit_mode ();
301 MidiTimeAxisView::check_step_edit ()
303 ensure_step_editor ();
304 _step_editor->check_step_edit ();
308 MidiTimeAxisView::model_changed()
310 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
311 .custom_device_mode_names_by_model(_model_selector.get_active_text());
313 _custom_device_mode_selector.clear_items();
315 for (std::list<std::string>::const_iterator i = device_modes.begin();
316 i != device_modes.end(); ++i) {
317 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
318 _custom_device_mode_selector.append_text(*i);
321 _custom_device_mode_selector.set_active(0);
324 void MidiTimeAxisView::custom_device_mode_changed()
326 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
327 _custom_device_mode_selector.get_active_text());
331 MidiTimeAxisView::midi_view()
333 return dynamic_cast<MidiStreamView*>(_view);
337 MidiTimeAxisView::set_height (uint32_t h)
339 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
340 _midi_controls_box.show_all ();
342 _midi_controls_box.hide();
345 if (h >= KEYBOARD_MIN_HEIGHT) {
346 if (is_track() && _range_scroomer) {
347 _range_scroomer->show();
349 if (is_track() && _piano_roll_header) {
350 _piano_roll_header->show();
353 if (is_track() && _range_scroomer) {
354 _range_scroomer->hide();
356 if (is_track() && _piano_roll_header) {
357 _piano_roll_header->hide();
361 /* We need to do this after changing visibility of our stuff, as it will
362 eventually trigger a call to Editor::reset_controls_layout_width(),
363 which needs to know if we have just shown or hidden a scroomer /
366 RouteTimeAxisView::set_height (h);
370 MidiTimeAxisView::append_extra_display_menu_items ()
372 using namespace Menu_Helpers;
374 MenuList& items = display_menu->items();
377 Menu *range_menu = manage(new Menu);
378 MenuList& range_items = range_menu->items();
379 range_menu->set_name ("ArdourContextMenu");
381 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
382 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
383 MidiStreamView::FullRange)));
385 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
386 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
387 MidiStreamView::ContentsRange)));
389 items.push_back (MenuElem (_("Note Range"), *range_menu));
390 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
392 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
393 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
395 items.push_back (SeparatorElem ());
399 MidiTimeAxisView::toggle_midi_thru ()
401 if (!_midi_thru_item) {
405 bool view_yn = _midi_thru_item->get_active();
406 if (view_yn != midi_track()->midi_thru()) {
407 midi_track()->set_midi_thru (view_yn);
412 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
414 using namespace Menu_Helpers;
416 /* If we have a controller menu, we need to detach it before
417 RouteTimeAxis::build_automation_action_menu destroys the
418 menu it is attached to. Otherwise GTK destroys
419 controller_menu's gobj, meaning that it can't be reattached
420 below. See bug #3134.
423 if (controller_menu) {
424 detach_menu (*controller_menu);
427 _channel_command_menu_map.clear ();
428 RouteTimeAxisView::build_automation_action_menu (for_selection);
430 MenuList& automation_items = automation_action_menu->items();
432 uint16_t selected_channels = _channel_selector.get_selected_channels();
434 if (selected_channels != 0) {
436 automation_items.push_back (SeparatorElem());
438 /* these 2 MIDI "command" types are semantically more like automation than note data,
439 but they are not MIDI controllers. We give them special status in this menu, since
440 they will not show up in the controller list and anyone who actually knows
441 something about MIDI (!) would not expect to find them there.
444 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
445 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
446 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
447 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
449 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
450 since it might need to be updated after a channel mode change or other change. Also detach it
451 first in case it has been used anywhere else.
454 build_controller_menu ();
456 automation_items.push_back (SeparatorElem());
457 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
458 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
460 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
461 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
467 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
469 uint16_t selected_channels = _channel_selector.get_selected_channels();
471 for (uint8_t chn = 0; chn < 16; chn++) {
472 if (selected_channels & (0x0001 << chn)) {
474 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
475 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
478 menu->set_active (yn);
485 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
487 using namespace Menu_Helpers;
489 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
492 uint16_t selected_channels = _channel_selector.get_selected_channels();
495 for (uint8_t chn = 0; chn < 16; chn++) {
496 if (selected_channels & (0x0001 << chn)) {
505 /* multiple channels - create a submenu, with 1 item per channel */
507 Menu* chn_menu = manage (new Menu);
508 MenuList& chn_items (chn_menu->items());
509 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
511 /* add a couple of items to hide/show all of them */
513 chn_items.push_back (MenuElem (_("Hide all channels"),
514 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
515 false, param_without_channel)));
516 chn_items.push_back (MenuElem (_("Show all channels"),
517 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
518 true, param_without_channel)));
520 for (uint8_t chn = 0; chn < 16; chn++) {
521 if (selected_channels & (0x0001 << chn)) {
523 /* for each selected channel, add a menu item for this controller */
525 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
526 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
527 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
528 fully_qualified_param)));
530 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
531 bool visible = false;
534 if (track->marked_for_display()) {
539 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
540 _channel_command_menu_map[fully_qualified_param] = cmi;
541 cmi->set_active (visible);
545 /* now create an item in the parent menu that has the per-channel list as a submenu */
547 items.push_back (MenuElem (label, *chn_menu));
551 /* just one channel - create a single menu item for this command+channel combination*/
553 for (uint8_t chn = 0; chn < 16; chn++) {
554 if (selected_channels & (0x0001 << chn)) {
556 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
557 items.push_back (CheckMenuElem (label,
558 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
559 fully_qualified_param)));
561 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
562 bool visible = false;
565 if (track->marked_for_display()) {
570 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
571 _channel_command_menu_map[fully_qualified_param] = cmi;
572 cmi->set_active (visible);
574 /* one channel only */
582 MidiTimeAxisView::build_controller_menu ()
584 using namespace Menu_Helpers;
586 if (controller_menu) {
587 /* it exists and has not been invalidated by a channel mode change, so just return it */
591 controller_menu = new Menu; // explicitly managed by us
592 MenuList& items (controller_menu->items());
594 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
595 for each controller+channel combination covering the currently selected channels for this track
598 uint16_t selected_channels = _channel_selector.get_selected_channels();
600 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
605 for (uint8_t chn = 0; chn < 16; chn++) {
606 if (selected_channels & (0x0001 << chn)) {
613 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
614 bank select controllers, as they are handled by the `patch' code */
616 for (int i = 0; i < 127; i += 16) {
618 Menu* ctl_menu = manage (new Menu);
619 MenuList& ctl_items (ctl_menu->items());
622 /* for each controller, consider whether to create a submenu or a single item */
624 for (int ctl = i; ctl < i+16; ++ctl) {
626 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
632 /* multiple channels - create a submenu, with 1 item per channel */
634 Menu* chn_menu = manage (new Menu);
635 MenuList& chn_items (chn_menu->items());
637 /* add a couple of items to hide/show this controller on all channels */
639 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
640 chn_items.push_back (MenuElem (_("Hide all channels"),
641 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
642 false, param_without_channel)));
643 chn_items.push_back (MenuElem (_("Show all channels"),
644 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
645 true, param_without_channel)));
647 for (uint8_t chn = 0; chn < 16; chn++) {
648 if (selected_channels & (0x0001 << chn)) {
650 /* for each selected channel, add a menu item for this controller */
652 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
653 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
654 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
655 fully_qualified_param)));
657 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
658 bool visible = false;
661 if (track->marked_for_display()) {
666 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
667 _controller_menu_map[fully_qualified_param] = cmi;
668 cmi->set_active (visible);
672 /* add the per-channel menu to the list of controllers, with the name of the controller */
673 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
674 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
678 /* just one channel - create a single menu item for this ctl+channel combination*/
680 for (uint8_t chn = 0; chn < 16; chn++) {
681 if (selected_channels & (0x0001 << chn)) {
683 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
684 ctl_items.push_back (
686 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
687 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
688 fully_qualified_param)
691 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
693 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
694 bool visible = false;
697 if (track->marked_for_display()) {
702 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
703 _controller_menu_map[fully_qualified_param] = cmi;
704 cmi->set_active (visible);
706 /* one channel only */
713 /* add the menu for this block of controllers to the overall controller menu */
715 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
720 MidiTimeAxisView::build_note_mode_menu()
722 using namespace Menu_Helpers;
724 Menu* mode_menu = manage (new Menu);
725 MenuList& items = mode_menu->items();
726 mode_menu->set_name ("ArdourContextMenu");
728 RadioMenuItem::Group mode_group;
729 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
730 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
731 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
732 _note_mode_item->set_active(_note_mode == Sustained);
734 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
735 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
736 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
737 _percussion_mode_item->set_active(_note_mode == Percussive);
743 MidiTimeAxisView::build_color_mode_menu()
745 using namespace Menu_Helpers;
747 Menu* mode_menu = manage (new Menu);
748 MenuList& items = mode_menu->items();
749 mode_menu->set_name ("ArdourContextMenu");
751 RadioMenuItem::Group mode_group;
752 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
753 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
754 MeterColors, false, true)));
755 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
756 _meter_color_mode_item->set_active(_color_mode == MeterColors);
758 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
759 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
760 ChannelColors, false, true)));
761 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
762 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
764 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
765 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
766 TrackColor, false, true)));
767 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
768 _channel_color_mode_item->set_active(_color_mode == TrackColor);
774 MidiTimeAxisView::set_note_mode(NoteMode mode)
776 if (_note_mode != mode || midi_track()->note_mode() != mode) {
778 midi_track()->set_note_mode(mode);
779 set_gui_property ("note-mode", enum_2_string(_note_mode));
780 _view->redisplay_track();
785 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
787 if (_color_mode == mode && !force) {
791 if (mode == ChannelColors) {
792 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
794 _channel_selector.set_default_channel_color();
798 set_gui_property ("color-mode", enum_2_string(_color_mode));
800 _view->redisplay_track();
805 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
807 if (!_ignore_signals)
808 midi_view()->set_note_range(range);
813 MidiTimeAxisView::update_range()
815 MidiGhostRegion* mgr;
817 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
818 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
825 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
827 if (apply_to_selection) {
828 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
831 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
833 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
834 create_automation_child(*i, true);
838 RouteTimeAxisView::show_all_automation ();
843 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
845 if (apply_to_selection) {
846 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
849 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
851 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
852 create_automation_child (*i, true);
856 RouteTimeAxisView::show_existing_automation ();
860 /** Create an automation track for the given parameter (pitch bend, channel pressure).
863 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
865 if (param.type() == NullAutomation) {
866 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
870 AutomationTracks::iterator existing = _automation_tracks.find (param);
872 if (existing != _automation_tracks.end()) {
874 /* automation track created because we had existing data for
875 * the processor, but visibility may need to be controlled
876 * since it will have been set visible by default.
879 cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
881 if (existing->second->set_marked_for_display (show) && !no_redraw) {
888 boost::shared_ptr<AutomationTimeAxisView> track;
890 switch (param.type()) {
893 create_gain_automation_child (param, show);
896 case PluginAutomation:
897 /* handled elsewhere */
900 case MidiCCAutomation:
901 case MidiPgmChangeAutomation:
902 case MidiPitchBenderAutomation:
903 case MidiChannelPressureAutomation:
904 case MidiSystemExclusiveAutomation:
905 /* These controllers are region "automation" - they are owned
906 * by regions (and their MidiModels), not by the track. As a
907 * result we do not create an AutomationList/Line for the track
908 * ... except here we are doing something!! XXX
911 track.reset (new AutomationTimeAxisView (
914 boost::shared_ptr<Automatable> (),
915 boost::shared_ptr<AutomationControl> (),
921 _route->describe_parameter(param)
925 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
928 add_automation_child (param, track, show);
932 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
937 MidiTimeAxisView::route_active_changed ()
939 RouteUI::route_active_changed ();
942 if (_route->active()) {
943 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
944 controls_base_selected_name = "MidiTrackControlsBaseSelected";
945 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
947 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
948 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
949 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
955 if (_route->active()) {
956 controls_ebox.set_name ("BusControlsBaseUnselected");
957 controls_base_selected_name = "BusControlsBaseSelected";
958 controls_base_unselected_name = "BusControlsBaseUnselected";
960 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
961 controls_base_selected_name = "BusControlsBaseInactiveSelected";
962 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
970 MidiTimeAxisView::add_note_selection (uint8_t note)
972 if (!_editor.internal_editing()) {
976 uint16_t chn_mask = _channel_selector.get_selected_channels();
978 if (_view->num_selected_regionviews() == 0) {
979 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
981 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
986 MidiTimeAxisView::extend_note_selection (uint8_t note)
988 if (!_editor.internal_editing()) {
992 uint16_t chn_mask = _channel_selector.get_selected_channels();
994 if (_view->num_selected_regionviews() == 0) {
995 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
997 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1002 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1004 if (!_editor.internal_editing()) {
1008 uint16_t chn_mask = _channel_selector.get_selected_channels();
1010 if (_view->num_selected_regionviews() == 0) {
1011 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1013 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1018 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1020 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1024 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1026 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1030 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1032 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1036 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1038 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1042 uint16_t selected_channels = _channel_selector.get_selected_channels();
1043 bool changed = false;
1047 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1049 for (uint32_t chn = 0; chn < 16; ++chn) {
1050 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1051 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1057 if ((selected_channels & (0x0001 << chn)) == 0) {
1058 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1059 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1061 changed = track->set_marked_for_display (false) || changed;
1063 changed = track->set_marked_for_display (true) || changed;
1070 /* TODO: Bender, Pressure */
1072 /* invalidate the controller menu, so that we rebuild it next time */
1073 _controller_menu_map.clear ();
1074 delete controller_menu;
1075 controller_menu = 0;
1083 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1085 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1090 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1091 if (i != _controller_menu_map.end()) {
1095 i = _channel_command_menu_map.find (param);
1096 if (i != _channel_command_menu_map.end()) {
1103 boost::shared_ptr<MidiRegion>
1104 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1106 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1108 real_editor->begin_reversible_command (Operations::create_region);
1109 playlist()->clear_changes ();
1111 real_editor->snap_to (pos, 0);
1113 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1114 view()->trackview().track()->name());
1117 plist.add (ARDOUR::Properties::start, 0);
1118 plist.add (ARDOUR::Properties::length, length);
1119 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1121 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1123 playlist()->add_region (region, pos);
1124 _session->add_command (new StatefulDiffCommand (playlist()));
1127 real_editor->commit_reversible_command ();
1130 return boost::dynamic_pointer_cast<MidiRegion>(region);
1134 MidiTimeAxisView::ensure_step_editor ()
1136 if (!_step_editor) {
1137 _step_editor = new StepEditor (_editor, midi_track(), *this);
1142 MidiTimeAxisView::start_step_editing ()
1144 ensure_step_editor ();
1145 _step_editor->start_step_editing ();
1149 MidiTimeAxisView::stop_step_editing ()
1152 _step_editor->stop_step_editing ();
1157 /** @return channel (counted from 0) to add an event to, based on the current setting
1158 * of the channel selector.
1161 MidiTimeAxisView::get_channel_for_add () const
1163 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1165 uint8_t channel = 0;
1167 /* pick the highest selected channel, unless all channels are selected,
1168 which is interpreted to mean channel 1 (zero)
1171 for (uint16_t i = 0; i < 16; ++i) {
1172 if (chn_mask & (1<<i)) {
1178 if (chn_cnt == 16) {
1186 MidiTimeAxisView::note_range_changed ()
1188 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1189 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());