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/event_type_map.h"
42 #include "ardour/midi_patch_manager.h"
43 #include "ardour/midi_playlist.h"
44 #include "ardour/midi_region.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/midi_track.h"
47 #include "ardour/operations.h"
48 #include "ardour/playlist.h"
49 #include "ardour/region.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/route.h"
52 #include "ardour/session.h"
53 #include "ardour/session_object.h"
54 #include "ardour/source.h"
55 #include "ardour/track.h"
56 #include "ardour/types.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"
68 #include "ghostregion.h"
69 #include "gui_thread.h"
71 #include "midi_scroomer.h"
72 #include "midi_streamview.h"
73 #include "midi_region_view.h"
74 #include "midi_time_axis.h"
75 #include "piano_roll_header.h"
76 #include "playlist_selector.h"
77 #include "plugin_selector.h"
78 #include "plugin_ui.h"
79 #include "point_selection.h"
81 #include "region_view.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "step_editor.h"
85 #include "simplerect.h"
88 #include "ardour/midi_track.h"
92 using namespace ARDOUR;
95 using namespace Gtkmm2ext;
96 using namespace Editing;
98 // Minimum height at which a control is displayed
99 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
100 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
102 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
103 : AxisView(sess) // virtually inherited
104 , RouteTimeAxisView(ed, sess, canvas)
105 , _ignore_signals(false)
107 , _piano_roll_header(0)
108 , _note_mode(Sustained)
110 , _percussion_mode_item(0)
111 , _color_mode(MeterColors)
112 , _meter_color_mode_item(0)
113 , _channel_color_mode_item(0)
114 , _track_color_mode_item(0)
115 , _step_edit_item (0)
116 , _midi_thru_item (0)
117 , controller_menu (0)
123 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
127 _view = new MidiStreamView (*this);
130 _piano_roll_header = new PianoRollHeader(*midi_view());
131 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
134 /* This next call will result in our height being set up, so it must come after
135 the creation of the piano roll / range scroomer as their visibility is set up
138 RouteTimeAxisView::set_route (rt);
140 _view->apply_color (_color, StreamView::RegionColor);
142 subplugin_menu.set_name ("ArdourContextMenu");
144 if (!gui_property ("note-range-min").empty ()) {
145 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
147 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
149 ignore_toggle = false;
151 if (is_midi_track()) {
152 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
153 _note_mode = midi_track()->note_mode();
154 } else { // MIDI bus (which doesn't exist yet..)
155 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
158 /* map current state of the route */
160 processors_changed (RouteProcessorChange ());
162 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
165 _piano_roll_header->SetNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
166 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
167 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
168 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
170 /* Suspend updates of the StreamView during scroomer drags to speed things up */
171 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
172 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
174 controls_hbox.pack_start(*_range_scroomer);
175 controls_hbox.pack_start(*_piano_roll_header);
177 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
178 controls_base_selected_name = "MidiTrackControlsBaseSelected";
179 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
181 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
183 /* ask for notifications of any new RegionViews */
184 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
186 if (!_editor.have_idled()) {
187 /* first idle will do what we need */
193 HBox* midi_controls_hbox = manage(new HBox());
195 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
197 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
198 for (; m != patch_manager.all_models().end(); ++m) {
199 _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 _model_selector.set_active_text (gui_property (X_("midnam-model-name")));
209 _custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
211 midi_controls_hbox->pack_start(_channel_selector, true, false);
212 if (!patch_manager.all_models().empty()) {
213 _midi_controls_box.pack_start(_model_selector, true, false);
214 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
217 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
219 controls_vbox.pack_start(_midi_controls_box, false, false);
221 // restore channel selector settings
222 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
223 _channel_selector.mode_changed.connect(
224 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
225 _channel_selector.mode_changed.connect(
226 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
228 string prop = gui_property ("color-mode");
230 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
231 if (_color_mode == ChannelColors) {
232 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
236 set_color_mode (_color_mode, true, false);
238 prop = gui_property ("note-mode");
240 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
241 if (_percussion_mode_item) {
242 _percussion_mode_item->set_active (_note_mode == Percussive);
246 /* Look for any GUI object state nodes that represent automation children that should exist, and create
250 list<string> gui_ids = gui_object_state().all_ids ();
251 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
254 Evoral::Parameter parameter (0, 0, 0);
256 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
257 if (p && route_id == _route->id () && has_parameter) {
258 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
264 MidiTimeAxisView::first_idle ()
271 MidiTimeAxisView::~MidiTimeAxisView ()
273 delete _piano_roll_header;
274 _piano_roll_header = 0;
276 delete _range_scroomer;
279 delete controller_menu;
284 MidiTimeAxisView::enter_internal_edit_mode ()
287 midi_view()->enter_internal_edit_mode ();
292 MidiTimeAxisView::leave_internal_edit_mode ()
295 midi_view()->leave_internal_edit_mode ();
300 MidiTimeAxisView::check_step_edit ()
302 ensure_step_editor ();
303 _step_editor->check_step_edit ();
307 MidiTimeAxisView::model_changed()
309 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
310 .custom_device_mode_names_by_model(_model_selector.get_active_text());
312 _custom_device_mode_selector.clear_items();
314 for (std::list<std::string>::const_iterator i = device_modes.begin();
315 i != device_modes.end(); ++i) {
316 _custom_device_mode_selector.append_text(*i);
319 _custom_device_mode_selector.set_active(0);
321 set_gui_property (X_("midnam-model-name"), midi_patch_model ());
325 MidiTimeAxisView::custom_device_mode_changed()
327 _midi_patch_settings_changed.emit (midi_patch_model (), midi_patch_custom_device_mode ());
328 set_gui_property (X_("midnam-custom-device-mode"), midi_patch_custom_device_mode ());
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) {
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 if (existing->second->set_marked_for_display (show) && !no_redraw) {
886 boost::shared_ptr<AutomationTimeAxisView> track;
888 switch (param.type()) {
891 create_gain_automation_child (param, show);
894 case PluginAutomation:
895 /* handled elsewhere */
898 case MidiCCAutomation:
899 case MidiPgmChangeAutomation:
900 case MidiPitchBenderAutomation:
901 case MidiChannelPressureAutomation:
902 case MidiSystemExclusiveAutomation:
903 /* These controllers are region "automation" - they are owned
904 * by regions (and their MidiModels), not by the track. As a
905 * result we do not create an AutomationList/Line for the track
906 * ... except here we are doing something!! XXX
909 track.reset (new AutomationTimeAxisView (
912 boost::shared_ptr<Automatable> (),
913 boost::shared_ptr<AutomationControl> (),
919 _route->describe_parameter(param)
923 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
926 add_automation_child (param, track, show);
930 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
935 MidiTimeAxisView::route_active_changed ()
937 RouteUI::route_active_changed ();
940 if (_route->active()) {
941 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
942 controls_base_selected_name = "MidiTrackControlsBaseSelected";
943 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
945 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
946 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
947 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
953 if (_route->active()) {
954 controls_ebox.set_name ("BusControlsBaseUnselected");
955 controls_base_selected_name = "BusControlsBaseSelected";
956 controls_base_unselected_name = "BusControlsBaseUnselected";
958 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
959 controls_base_selected_name = "BusControlsBaseInactiveSelected";
960 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
966 MidiTimeAxisView::set_note_selection (uint8_t note)
968 if (!_editor.internal_editing()) {
972 uint16_t chn_mask = _channel_selector.get_selected_channels();
974 if (_view->num_selected_regionviews() == 0) {
975 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
977 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
982 MidiTimeAxisView::add_note_selection (uint8_t note)
984 if (!_editor.internal_editing()) {
988 uint16_t chn_mask = _channel_selector.get_selected_channels();
990 if (_view->num_selected_regionviews() == 0) {
991 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
993 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
998 MidiTimeAxisView::extend_note_selection (uint8_t note)
1000 if (!_editor.internal_editing()) {
1004 uint16_t chn_mask = _channel_selector.get_selected_channels();
1006 if (_view->num_selected_regionviews() == 0) {
1007 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1009 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1014 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1016 if (!_editor.internal_editing()) {
1020 uint16_t chn_mask = _channel_selector.get_selected_channels();
1022 if (_view->num_selected_regionviews() == 0) {
1023 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1025 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1030 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1032 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1036 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1038 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1042 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1044 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1048 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1050 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1054 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1056 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1060 uint16_t selected_channels = _channel_selector.get_selected_channels();
1061 bool changed = false;
1065 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1067 for (uint32_t chn = 0; chn < 16; ++chn) {
1068 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1069 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1075 if ((selected_channels & (0x0001 << chn)) == 0) {
1076 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1077 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1079 changed = track->set_marked_for_display (false) || changed;
1081 changed = track->set_marked_for_display (true) || changed;
1088 /* TODO: Bender, Pressure */
1090 /* invalidate the controller menu, so that we rebuild it next time */
1091 _controller_menu_map.clear ();
1092 delete controller_menu;
1093 controller_menu = 0;
1101 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1103 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1108 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1109 if (i != _controller_menu_map.end()) {
1113 i = _channel_command_menu_map.find (param);
1114 if (i != _channel_command_menu_map.end()) {
1121 boost::shared_ptr<MidiRegion>
1122 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1124 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1126 real_editor->begin_reversible_command (Operations::create_region);
1127 playlist()->clear_changes ();
1129 real_editor->snap_to (pos, 0);
1131 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1132 view()->trackview().track()->name());
1135 plist.add (ARDOUR::Properties::start, 0);
1136 plist.add (ARDOUR::Properties::length, length);
1137 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1139 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1141 playlist()->add_region (region, pos);
1142 _session->add_command (new StatefulDiffCommand (playlist()));
1145 real_editor->commit_reversible_command ();
1148 return boost::dynamic_pointer_cast<MidiRegion>(region);
1152 MidiTimeAxisView::ensure_step_editor ()
1154 if (!_step_editor) {
1155 _step_editor = new StepEditor (_editor, midi_track(), *this);
1160 MidiTimeAxisView::start_step_editing ()
1162 ensure_step_editor ();
1163 _step_editor->start_step_editing ();
1167 MidiTimeAxisView::stop_step_editing ()
1170 _step_editor->stop_step_editing ();
1175 /** @return channel (counted from 0) to add an event to, based on the current setting
1176 * of the channel selector.
1179 MidiTimeAxisView::get_channel_for_add () const
1181 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1183 uint8_t channel = 0;
1185 /* pick the highest selected channel, unless all channels are selected,
1186 which is interpreted to mean channel 1 (zero)
1189 for (uint16_t i = 0; i < 16; ++i) {
1190 if (chn_mask & (1<<i)) {
1196 if (chn_cnt == 16) {
1204 MidiTimeAxisView::note_range_changed ()
1206 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1207 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1211 MidiTimeAxisView::midi_patch_model () const
1213 return _model_selector.get_active_text ();
1217 MidiTimeAxisView::midi_patch_custom_device_mode () const
1219 return _custom_device_mode_selector.get_active_text ();
1223 MidiTimeAxisView::get_patch_name (uint16_t bank, uint8_t program, uint8_t channel) const
1225 MIDI::Name::PatchPrimaryKey patch_key (bank, program);
1227 boost::shared_ptr<MIDI::Name::Patch> patch =
1228 MIDI::Name::MidiPatchManager::instance().find_patch (midi_patch_model(), midi_patch_custom_device_mode(), channel, patch_key);
1231 return patch->name();
1233 /* program and bank numbers are zero-based: convert to one-based: MIDI_BP_ZERO */
1235 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
1237 return string_compose ("%1 %2",program + MIDI_BP_ZERO , bank + MIDI_BP_ZERO);