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->SetNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
168 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
169 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
170 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
172 /* Suspend updates of the StreamView during scroomer drags to speed things up */
173 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
174 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
176 controls_hbox.pack_start(*_range_scroomer);
177 controls_hbox.pack_start(*_piano_roll_header);
179 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
180 controls_base_selected_name = "MidiTrackControlsBaseSelected";
181 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
183 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
185 /* ask for notifications of any new RegionViews */
186 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
188 if (!_editor.have_idled()) {
189 /* first idle will do what we need */
195 HBox* midi_controls_hbox = manage(new HBox());
197 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
199 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
200 for (; m != patch_manager.all_models().end(); ++m) {
201 _model_selector.append_text(m->c_str());
204 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
206 _custom_device_mode_selector.signal_changed().connect(
207 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
209 // TODO: persist the choice
210 // this initializes the comboboxes and sends out the signal
211 _model_selector.set_active(0);
213 midi_controls_hbox->pack_start(_channel_selector, true, false);
214 if (!patch_manager.all_models().empty()) {
215 _midi_controls_box.pack_start(_model_selector, true, false);
216 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
219 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
221 controls_vbox.pack_start(_midi_controls_box, false, false);
223 // restore channel selector settings
224 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
225 _channel_selector.mode_changed.connect(
226 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
227 _channel_selector.mode_changed.connect(
228 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
230 string prop = gui_property ("color-mode");
232 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
233 if (_color_mode == ChannelColors) {
234 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
238 set_color_mode (_color_mode, true, false);
240 prop = gui_property ("note-mode");
242 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
243 if (_percussion_mode_item) {
244 _percussion_mode_item->set_active (_note_mode == Percussive);
248 /* Look for any GUI object state nodes that represent automation children that should exist, and create
252 list<string> gui_ids = gui_object_state().all_ids ();
253 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
256 Evoral::Parameter parameter (0, 0, 0);
258 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
259 if (p && route_id == _route->id () && has_parameter) {
260 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
266 MidiTimeAxisView::first_idle ()
273 MidiTimeAxisView::~MidiTimeAxisView ()
275 delete _piano_roll_header;
276 _piano_roll_header = 0;
278 delete _range_scroomer;
281 delete controller_menu;
286 MidiTimeAxisView::enter_internal_edit_mode ()
289 midi_view()->enter_internal_edit_mode ();
294 MidiTimeAxisView::leave_internal_edit_mode ()
297 midi_view()->leave_internal_edit_mode ();
302 MidiTimeAxisView::check_step_edit ()
304 ensure_step_editor ();
305 _step_editor->check_step_edit ();
309 MidiTimeAxisView::model_changed()
311 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
312 .custom_device_mode_names_by_model(_model_selector.get_active_text());
314 _custom_device_mode_selector.clear_items();
316 for (std::list<std::string>::const_iterator i = device_modes.begin();
317 i != device_modes.end(); ++i) {
318 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
319 _custom_device_mode_selector.append_text(*i);
322 _custom_device_mode_selector.set_active(0);
325 void MidiTimeAxisView::custom_device_mode_changed()
327 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
328 _custom_device_mode_selector.get_active_text());
332 MidiTimeAxisView::midi_view()
334 return dynamic_cast<MidiStreamView*>(_view);
338 MidiTimeAxisView::set_height (uint32_t h)
340 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
341 _midi_controls_box.show_all ();
343 _midi_controls_box.hide();
346 if (h >= KEYBOARD_MIN_HEIGHT) {
347 if (is_track() && _range_scroomer) {
348 _range_scroomer->show();
350 if (is_track() && _piano_roll_header) {
351 _piano_roll_header->show();
354 if (is_track() && _range_scroomer) {
355 _range_scroomer->hide();
357 if (is_track() && _piano_roll_header) {
358 _piano_roll_header->hide();
362 /* We need to do this after changing visibility of our stuff, as it will
363 eventually trigger a call to Editor::reset_controls_layout_width(),
364 which needs to know if we have just shown or hidden a scroomer /
367 RouteTimeAxisView::set_height (h);
371 MidiTimeAxisView::append_extra_display_menu_items ()
373 using namespace Menu_Helpers;
375 MenuList& items = display_menu->items();
378 Menu *range_menu = manage(new Menu);
379 MenuList& range_items = range_menu->items();
380 range_menu->set_name ("ArdourContextMenu");
382 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
383 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
384 MidiStreamView::FullRange)));
386 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
387 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
388 MidiStreamView::ContentsRange)));
390 items.push_back (MenuElem (_("Note Range"), *range_menu));
391 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
393 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
394 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
396 items.push_back (SeparatorElem ());
400 MidiTimeAxisView::toggle_midi_thru ()
402 if (!_midi_thru_item) {
406 bool view_yn = _midi_thru_item->get_active();
407 if (view_yn != midi_track()->midi_thru()) {
408 midi_track()->set_midi_thru (view_yn);
413 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
415 using namespace Menu_Helpers;
417 /* If we have a controller menu, we need to detach it before
418 RouteTimeAxis::build_automation_action_menu destroys the
419 menu it is attached to. Otherwise GTK destroys
420 controller_menu's gobj, meaning that it can't be reattached
421 below. See bug #3134.
424 if (controller_menu) {
425 detach_menu (*controller_menu);
428 _channel_command_menu_map.clear ();
429 RouteTimeAxisView::build_automation_action_menu (for_selection);
431 MenuList& automation_items = automation_action_menu->items();
433 uint16_t selected_channels = _channel_selector.get_selected_channels();
435 if (selected_channels != 0) {
437 automation_items.push_back (SeparatorElem());
439 /* these 2 MIDI "command" types are semantically more like automation than note data,
440 but they are not MIDI controllers. We give them special status in this menu, since
441 they will not show up in the controller list and anyone who actually knows
442 something about MIDI (!) would not expect to find them there.
445 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
446 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
447 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
448 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
450 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
451 since it might need to be updated after a channel mode change or other change. Also detach it
452 first in case it has been used anywhere else.
455 build_controller_menu ();
457 automation_items.push_back (SeparatorElem());
458 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
459 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
461 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
462 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
468 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
470 uint16_t selected_channels = _channel_selector.get_selected_channels();
472 for (uint8_t chn = 0; chn < 16; chn++) {
473 if (selected_channels & (0x0001 << chn)) {
475 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
476 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
479 menu->set_active (yn);
486 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
488 using namespace Menu_Helpers;
490 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
493 uint16_t selected_channels = _channel_selector.get_selected_channels();
496 for (uint8_t chn = 0; chn < 16; chn++) {
497 if (selected_channels & (0x0001 << chn)) {
506 /* multiple channels - create a submenu, with 1 item per channel */
508 Menu* chn_menu = manage (new Menu);
509 MenuList& chn_items (chn_menu->items());
510 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
512 /* add a couple of items to hide/show all of them */
514 chn_items.push_back (MenuElem (_("Hide all channels"),
515 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
516 false, param_without_channel)));
517 chn_items.push_back (MenuElem (_("Show all channels"),
518 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
519 true, param_without_channel)));
521 for (uint8_t chn = 0; chn < 16; chn++) {
522 if (selected_channels & (0x0001 << chn)) {
524 /* for each selected channel, add a menu item for this controller */
526 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
527 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
528 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
529 fully_qualified_param)));
531 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
532 bool visible = false;
535 if (track->marked_for_display()) {
540 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
541 _channel_command_menu_map[fully_qualified_param] = cmi;
542 cmi->set_active (visible);
546 /* now create an item in the parent menu that has the per-channel list as a submenu */
548 items.push_back (MenuElem (label, *chn_menu));
552 /* just one channel - create a single menu item for this command+channel combination*/
554 for (uint8_t chn = 0; chn < 16; chn++) {
555 if (selected_channels & (0x0001 << chn)) {
557 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
558 items.push_back (CheckMenuElem (label,
559 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
560 fully_qualified_param)));
562 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
563 bool visible = false;
566 if (track->marked_for_display()) {
571 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
572 _channel_command_menu_map[fully_qualified_param] = cmi;
573 cmi->set_active (visible);
575 /* one channel only */
583 MidiTimeAxisView::build_controller_menu ()
585 using namespace Menu_Helpers;
587 if (controller_menu) {
588 /* it exists and has not been invalidated by a channel mode change, so just return it */
592 controller_menu = new Menu; // explicitly managed by us
593 MenuList& items (controller_menu->items());
595 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
596 for each controller+channel combination covering the currently selected channels for this track
599 uint16_t selected_channels = _channel_selector.get_selected_channels();
601 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
606 for (uint8_t chn = 0; chn < 16; chn++) {
607 if (selected_channels & (0x0001 << chn)) {
614 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
615 bank select controllers, as they are handled by the `patch' code */
617 for (int i = 0; i < 127; i += 16) {
619 Menu* ctl_menu = manage (new Menu);
620 MenuList& ctl_items (ctl_menu->items());
623 /* for each controller, consider whether to create a submenu or a single item */
625 for (int ctl = i; ctl < i+16; ++ctl) {
627 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
633 /* multiple channels - create a submenu, with 1 item per channel */
635 Menu* chn_menu = manage (new Menu);
636 MenuList& chn_items (chn_menu->items());
638 /* add a couple of items to hide/show this controller on all channels */
640 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
641 chn_items.push_back (MenuElem (_("Hide all channels"),
642 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
643 false, param_without_channel)));
644 chn_items.push_back (MenuElem (_("Show all channels"),
645 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
646 true, param_without_channel)));
648 for (uint8_t chn = 0; chn < 16; chn++) {
649 if (selected_channels & (0x0001 << chn)) {
651 /* for each selected channel, add a menu item for this controller */
653 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
654 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
655 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
656 fully_qualified_param)));
658 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
659 bool visible = false;
662 if (track->marked_for_display()) {
667 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
668 _controller_menu_map[fully_qualified_param] = cmi;
669 cmi->set_active (visible);
673 /* add the per-channel menu to the list of controllers, with the name of the controller */
674 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
675 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
679 /* just one channel - create a single menu item for this ctl+channel combination*/
681 for (uint8_t chn = 0; chn < 16; chn++) {
682 if (selected_channels & (0x0001 << chn)) {
684 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
685 ctl_items.push_back (
687 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
688 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
689 fully_qualified_param)
692 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
694 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
695 bool visible = false;
698 if (track->marked_for_display()) {
703 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
704 _controller_menu_map[fully_qualified_param] = cmi;
705 cmi->set_active (visible);
707 /* one channel only */
714 /* add the menu for this block of controllers to the overall controller menu */
716 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
721 MidiTimeAxisView::build_note_mode_menu()
723 using namespace Menu_Helpers;
725 Menu* mode_menu = manage (new Menu);
726 MenuList& items = mode_menu->items();
727 mode_menu->set_name ("ArdourContextMenu");
729 RadioMenuItem::Group mode_group;
730 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
731 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
732 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
733 _note_mode_item->set_active(_note_mode == Sustained);
735 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
736 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
737 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
738 _percussion_mode_item->set_active(_note_mode == Percussive);
744 MidiTimeAxisView::build_color_mode_menu()
746 using namespace Menu_Helpers;
748 Menu* mode_menu = manage (new Menu);
749 MenuList& items = mode_menu->items();
750 mode_menu->set_name ("ArdourContextMenu");
752 RadioMenuItem::Group mode_group;
753 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
754 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
755 MeterColors, false, true)));
756 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
757 _meter_color_mode_item->set_active(_color_mode == MeterColors);
759 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
760 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
761 ChannelColors, false, true)));
762 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
763 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
765 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
766 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
767 TrackColor, false, true)));
768 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
769 _channel_color_mode_item->set_active(_color_mode == TrackColor);
775 MidiTimeAxisView::set_note_mode(NoteMode mode)
777 if (_note_mode != mode || midi_track()->note_mode() != mode) {
779 midi_track()->set_note_mode(mode);
780 set_gui_property ("note-mode", enum_2_string(_note_mode));
781 _view->redisplay_track();
786 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
788 if (_color_mode == mode && !force) {
792 if (mode == ChannelColors) {
793 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
795 _channel_selector.set_default_channel_color();
799 set_gui_property ("color-mode", enum_2_string(_color_mode));
801 _view->redisplay_track();
806 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
808 if (!_ignore_signals)
809 midi_view()->set_note_range(range);
814 MidiTimeAxisView::update_range()
816 MidiGhostRegion* mgr;
818 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
819 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
826 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
828 if (apply_to_selection) {
829 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
832 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
834 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
835 create_automation_child(*i, true);
839 RouteTimeAxisView::show_all_automation ();
844 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
846 if (apply_to_selection) {
847 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
850 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
852 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
853 create_automation_child (*i, true);
857 RouteTimeAxisView::show_existing_automation ();
861 /** Create an automation track for the given parameter (pitch bend, channel pressure).
864 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
866 if (param.type() == NullAutomation) {
867 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
871 AutomationTracks::iterator existing = _automation_tracks.find (param);
873 if (existing != _automation_tracks.end()) {
875 /* automation track created because we had existing data for
876 * the processor, but visibility may need to be controlled
877 * since it will have been set visible by default.
880 cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
882 if (existing->second->set_marked_for_display (show) && !no_redraw) {
889 boost::shared_ptr<AutomationTimeAxisView> track;
891 switch (param.type()) {
894 create_gain_automation_child (param, show);
897 case PluginAutomation:
898 /* handled elsewhere */
901 case MidiCCAutomation:
902 case MidiPgmChangeAutomation:
903 case MidiPitchBenderAutomation:
904 case MidiChannelPressureAutomation:
905 case MidiSystemExclusiveAutomation:
906 /* These controllers are region "automation" - they are owned
907 * by regions (and their MidiModels), not by the track. As a
908 * result we do not create an AutomationList/Line for the track
909 * ... except here we are doing something!! XXX
912 track.reset (new AutomationTimeAxisView (
915 boost::shared_ptr<Automatable> (),
916 boost::shared_ptr<AutomationControl> (),
922 _route->describe_parameter(param)
926 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
929 add_automation_child (param, track, show);
933 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
938 MidiTimeAxisView::route_active_changed ()
940 RouteUI::route_active_changed ();
943 if (_route->active()) {
944 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
945 controls_base_selected_name = "MidiTrackControlsBaseSelected";
946 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
948 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
949 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
950 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
956 if (_route->active()) {
957 controls_ebox.set_name ("BusControlsBaseUnselected");
958 controls_base_selected_name = "BusControlsBaseSelected";
959 controls_base_unselected_name = "BusControlsBaseUnselected";
961 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
962 controls_base_selected_name = "BusControlsBaseInactiveSelected";
963 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
969 MidiTimeAxisView::set_note_selection (uint8_t note)
971 if (!_editor.internal_editing()) {
975 uint16_t chn_mask = _channel_selector.get_selected_channels();
977 if (_view->num_selected_regionviews() == 0) {
978 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
980 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
985 MidiTimeAxisView::add_note_selection (uint8_t note)
987 if (!_editor.internal_editing()) {
991 uint16_t chn_mask = _channel_selector.get_selected_channels();
993 if (_view->num_selected_regionviews() == 0) {
994 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
996 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1001 MidiTimeAxisView::extend_note_selection (uint8_t note)
1003 if (!_editor.internal_editing()) {
1007 uint16_t chn_mask = _channel_selector.get_selected_channels();
1009 if (_view->num_selected_regionviews() == 0) {
1010 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1012 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1017 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1019 if (!_editor.internal_editing()) {
1023 uint16_t chn_mask = _channel_selector.get_selected_channels();
1025 if (_view->num_selected_regionviews() == 0) {
1026 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1028 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1033 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1035 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1039 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1041 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1045 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1047 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1051 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1053 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1057 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1059 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1063 uint16_t selected_channels = _channel_selector.get_selected_channels();
1064 bool changed = false;
1068 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1070 for (uint32_t chn = 0; chn < 16; ++chn) {
1071 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1072 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1078 if ((selected_channels & (0x0001 << chn)) == 0) {
1079 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1080 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1082 changed = track->set_marked_for_display (false) || changed;
1084 changed = track->set_marked_for_display (true) || changed;
1091 /* TODO: Bender, Pressure */
1093 /* invalidate the controller menu, so that we rebuild it next time */
1094 _controller_menu_map.clear ();
1095 delete controller_menu;
1096 controller_menu = 0;
1104 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1106 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1111 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1112 if (i != _controller_menu_map.end()) {
1116 i = _channel_command_menu_map.find (param);
1117 if (i != _channel_command_menu_map.end()) {
1124 boost::shared_ptr<MidiRegion>
1125 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1127 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1129 real_editor->begin_reversible_command (Operations::create_region);
1130 playlist()->clear_changes ();
1132 real_editor->snap_to (pos, 0);
1134 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1135 view()->trackview().track()->name());
1138 plist.add (ARDOUR::Properties::start, 0);
1139 plist.add (ARDOUR::Properties::length, length);
1140 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1142 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1144 playlist()->add_region (region, pos);
1145 _session->add_command (new StatefulDiffCommand (playlist()));
1148 real_editor->commit_reversible_command ();
1151 return boost::dynamic_pointer_cast<MidiRegion>(region);
1155 MidiTimeAxisView::ensure_step_editor ()
1157 if (!_step_editor) {
1158 _step_editor = new StepEditor (_editor, midi_track(), *this);
1163 MidiTimeAxisView::start_step_editing ()
1165 ensure_step_editor ();
1166 _step_editor->start_step_editing ();
1170 MidiTimeAxisView::stop_step_editing ()
1173 _step_editor->stop_step_editing ();
1178 /** @return channel (counted from 0) to add an event to, based on the current setting
1179 * of the channel selector.
1182 MidiTimeAxisView::get_channel_for_add () const
1184 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1186 uint8_t channel = 0;
1188 /* pick the highest selected channel, unless all channels are selected,
1189 which is interpreted to mean channel 1 (zero)
1192 for (uint16_t i = 0; i < 16; ++i) {
1193 if (chn_mask & (1<<i)) {
1199 if (chn_cnt == 16) {
1207 MidiTimeAxisView::note_range_changed ()
1209 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1210 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());