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 "ardour_ui.h"
61 #include "ardour_button.h"
62 #include "automation_line.h"
63 #include "automation_time_axis.h"
64 #include "canvas-note-event.h"
65 #include "canvas_impl.h"
66 #include "crossfade_view.h"
69 #include "ghostregion.h"
70 #include "gui_thread.h"
72 #include "midi_scroomer.h"
73 #include "midi_streamview.h"
74 #include "midi_region_view.h"
75 #include "midi_time_axis.h"
76 #include "piano_roll_header.h"
77 #include "playlist_selector.h"
78 #include "plugin_selector.h"
79 #include "plugin_ui.h"
80 #include "point_selection.h"
82 #include "region_view.h"
83 #include "rgb_macros.h"
84 #include "selection.h"
85 #include "step_editor.h"
86 #include "simplerect.h"
89 #include "ardour/midi_track.h"
93 using namespace ARDOUR;
96 using namespace Gtkmm2ext;
97 using namespace Editing;
99 // Minimum height at which a control is displayed
100 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
101 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
103 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
104 : AxisView(sess) // virtually inherited
105 , RouteTimeAxisView(ed, sess, canvas)
106 , _ignore_signals(false)
108 , _piano_roll_header(0)
109 , _note_mode(Sustained)
111 , _percussion_mode_item(0)
112 , _color_mode(MeterColors)
113 , _meter_color_mode_item(0)
114 , _channel_color_mode_item(0)
115 , _track_color_mode_item(0)
116 , _step_edit_item (0)
117 , _midi_thru_item (0)
118 , controller_menu (0)
124 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
128 _view = new MidiStreamView (*this);
131 _piano_roll_header = new PianoRollHeader(*midi_view());
132 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
135 /* This next call will result in our height being set up, so it must come after
136 the creation of the piano roll / range scroomer as their visibility is set up
139 RouteTimeAxisView::set_route (rt);
141 _view->apply_color (_color, StreamView::RegionColor);
143 subplugin_menu.set_name ("ArdourContextMenu");
145 if (!gui_property ("note-range-min").empty ()) {
146 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
148 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
150 ignore_toggle = false;
152 if (is_midi_track()) {
153 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
154 _note_mode = midi_track()->note_mode();
155 } else { // MIDI bus (which doesn't exist yet..)
156 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
159 /* map current state of the route */
161 processors_changed (RouteProcessorChange ());
163 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
166 _piano_roll_header->SetNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
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 list<string> gui_ids = gui_object_state().all_ids ();
252 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
255 Evoral::Parameter parameter (0, 0, 0);
257 bool const p = AutomationTimeAxisView::parse_state_id (*i, 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, 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";
968 MidiTimeAxisView::set_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::set_note_selection_region_view), note, chn_mask));
979 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
984 MidiTimeAxisView::add_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::add_note_selection_region_view), note, chn_mask));
995 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1000 MidiTimeAxisView::extend_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::extend_note_selection_region_view), note, chn_mask));
1011 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1016 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1018 if (!_editor.internal_editing()) {
1022 uint16_t chn_mask = _channel_selector.get_selected_channels();
1024 if (_view->num_selected_regionviews() == 0) {
1025 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1027 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1032 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1034 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1038 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1040 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1044 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1046 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1050 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1052 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1056 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1058 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1062 uint16_t selected_channels = _channel_selector.get_selected_channels();
1063 bool changed = false;
1067 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1069 for (uint32_t chn = 0; chn < 16; ++chn) {
1070 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1071 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1077 if ((selected_channels & (0x0001 << chn)) == 0) {
1078 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1079 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1081 changed = track->set_marked_for_display (false) || changed;
1083 changed = track->set_marked_for_display (true) || changed;
1090 /* TODO: Bender, Pressure */
1092 /* invalidate the controller menu, so that we rebuild it next time */
1093 _controller_menu_map.clear ();
1094 delete controller_menu;
1095 controller_menu = 0;
1103 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1105 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1110 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1111 if (i != _controller_menu_map.end()) {
1115 i = _channel_command_menu_map.find (param);
1116 if (i != _channel_command_menu_map.end()) {
1123 boost::shared_ptr<MidiRegion>
1124 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1126 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1128 real_editor->begin_reversible_command (Operations::create_region);
1129 playlist()->clear_changes ();
1131 real_editor->snap_to (pos, 0);
1133 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1134 view()->trackview().track()->name());
1137 plist.add (ARDOUR::Properties::start, 0);
1138 plist.add (ARDOUR::Properties::length, length);
1139 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1141 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1143 playlist()->add_region (region, pos);
1144 _session->add_command (new StatefulDiffCommand (playlist()));
1147 real_editor->commit_reversible_command ();
1150 return boost::dynamic_pointer_cast<MidiRegion>(region);
1154 MidiTimeAxisView::ensure_step_editor ()
1156 if (!_step_editor) {
1157 _step_editor = new StepEditor (_editor, midi_track(), *this);
1162 MidiTimeAxisView::start_step_editing ()
1164 ensure_step_editor ();
1165 _step_editor->start_step_editing ();
1169 MidiTimeAxisView::stop_step_editing ()
1172 _step_editor->stop_step_editing ();
1177 /** @return channel (counted from 0) to add an event to, based on the current setting
1178 * of the channel selector.
1181 MidiTimeAxisView::get_channel_for_add () const
1183 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1185 uint8_t channel = 0;
1187 /* pick the highest selected channel, unless all channels are selected,
1188 which is interpreted to mean channel 1 (zero)
1191 for (uint16_t i = 0; i < 16; ++i) {
1192 if (chn_mask & (1<<i)) {
1198 if (chn_cnt == 16) {
1206 MidiTimeAxisView::note_range_changed ()
1208 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1209 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());