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 , controller_menu (0)
122 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
126 _view = new MidiStreamView (*this);
129 _piano_roll_header = new PianoRollHeader(*midi_view());
130 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
131 _range_scroomer->DoubleClicked.connect (
132 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
133 MidiStreamView::ContentsRange, false));
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()),
148 atoi (gui_property ("note-range-max").c_str()),
151 midi_view()->NoteRangeChanged.connect (
152 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
153 _view->ContentsHeightChanged.connect (
154 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
156 ignore_toggle = false;
158 if (is_midi_track()) {
159 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
160 _note_mode = midi_track()->note_mode();
161 } else { // MIDI bus (which doesn't exist yet..)
162 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
165 /* map current state of the route */
167 processors_changed (RouteProcessorChange ());
169 _route->processors_changed.connect (*this, invalidator (*this),
170 boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
174 _piano_roll_header->SetNoteSelection.connect (
175 sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
176 _piano_roll_header->AddNoteSelection.connect (
177 sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
178 _piano_roll_header->ExtendNoteSelection.connect (
179 sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
180 _piano_roll_header->ToggleNoteSelection.connect (
181 sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
183 /* Suspend updates of the StreamView during scroomer drags to speed things up */
184 _range_scroomer->DragStarting.connect (
185 sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
186 _range_scroomer->DragFinishing.connect (
187 sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
189 /* Put the scroomer and the keyboard in a VBox with a padding
190 label so that they can be reduced in height for stacked-view
193 VBox* v = manage (new VBox);
194 HBox* h = manage (new HBox);
195 h->pack_start (*_range_scroomer);
196 h->pack_start (*_piano_roll_header);
197 v->pack_start (*h, false, false);
198 v->pack_start (*manage (new Label ("")), true, true);
201 controls_hbox.pack_start(*v);
203 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
204 controls_base_selected_name = "MidiTrackControlsBaseSelected";
205 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
207 midi_view()->NoteRangeChanged.connect (
208 sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
210 /* ask for notifications of any new RegionViews */
211 _view->RegionViewAdded.connect (
212 sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
214 if (!_editor.have_idled()) {
215 /* first idle will do what we need */
222 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
224 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
225 for (; m != patch_manager.all_models().end(); ++m) {
226 _midnam_model_selector.append_text(m->c_str());
229 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
230 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
232 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
233 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
235 _midi_controls_box.set_homogeneous(false);
236 _midi_controls_box.set_border_width (10);
238 if (!patch_manager.all_models().empty()) {
239 _channel_selector.set_border_width(2);
240 _channel_selector.show_all ();
242 _midi_controls_box.resize(3, 2);
243 _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1);
245 _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2);
247 _midnam_model_selector.set_size_request(22, 30);
248 _midnam_model_selector.set_border_width(2);
249 _midnam_model_selector.show ();
250 _midi_controls_box.attach(_midnam_model_selector, 0, 1, 2, 3);
252 _midnam_custom_device_mode_selector.set_size_request(10, 30);
253 _midnam_custom_device_mode_selector.set_border_width(2);
254 _midnam_custom_device_mode_selector.show ();
255 _midi_controls_box.attach(_midnam_custom_device_mode_selector, 0, 1, 3, 4);
257 _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1);
258 _channel_selector.show_all ();
262 custom_device_mode_changed();
264 _midnam_model_selector.signal_changed().connect(
265 sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
266 _midnam_custom_device_mode_selector.signal_changed().connect(
267 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
270 controls_vbox.pack_start(_midi_controls_box, false, false);
272 // restore channel selector settings
273 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(),
274 midi_track()->get_channel_mask());
275 _channel_selector.mode_changed.connect(
276 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
277 _channel_selector.mode_changed.connect(
278 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
280 const string color_mode = gui_property ("color-mode");
281 if (!color_mode.empty()) {
282 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
283 if (_color_mode == ChannelColors) {
284 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
288 set_color_mode (_color_mode, true, false);
290 const string note_mode = gui_property ("note-mode");
291 if (!note_mode.empty()) {
292 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
293 if (_percussion_mode_item) {
294 _percussion_mode_item->set_active (_note_mode == Percussive);
298 /* Look for any GUI object state nodes that represent automation children
299 * that should exist, and create the children.
302 const list<string> gui_ids = gui_object_state().all_ids ();
303 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
306 Evoral::Parameter parameter (0, 0, 0);
308 bool const p = AutomationTimeAxisView::parse_state_id (
309 *i, route_id, has_parameter, parameter);
310 if (p && route_id == _route->id () && has_parameter) {
311 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
312 create_automation_child (parameter, string_is_affirmative (visible));
318 MidiTimeAxisView::first_idle ()
325 MidiTimeAxisView::~MidiTimeAxisView ()
327 delete _piano_roll_header;
328 _piano_roll_header = 0;
330 delete _range_scroomer;
333 delete controller_menu;
338 MidiTimeAxisView::enter_internal_edit_mode ()
341 midi_view()->enter_internal_edit_mode ();
346 MidiTimeAxisView::leave_internal_edit_mode ()
349 midi_view()->leave_internal_edit_mode ();
354 MidiTimeAxisView::check_step_edit ()
356 ensure_step_editor ();
357 _step_editor->check_step_edit ();
361 MidiTimeAxisView::model_changed()
363 const Glib::ustring model = _midnam_model_selector.get_active_text();
364 set_gui_property (X_("midnam-model-name"), model);
366 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
367 .custom_device_mode_names_by_model(model);
369 _midnam_custom_device_mode_selector.clear_items();
371 if (device_modes.size() < 2) {
372 _midnam_custom_device_mode_selector.hide();
374 _midnam_custom_device_mode_selector.show();
375 for (std::list<std::string>::const_iterator i = device_modes.begin();
376 i != device_modes.end(); ++i) {
377 _midnam_custom_device_mode_selector.append_text(*i);
381 _midnam_custom_device_mode_selector.set_active(0);
383 _route->instrument_info().set_external_instrument (
384 _midnam_model_selector.get_active_text(),
385 _midnam_custom_device_mode_selector.get_active_text());
387 // Rebuild controller menu
388 _controller_menu_map.clear ();
389 delete controller_menu;
391 build_automation_action_menu(false);
395 MidiTimeAxisView::custom_device_mode_changed()
397 const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text();
398 set_gui_property (X_("midnam-custom-device-mode"), mode);
399 _route->instrument_info().set_external_instrument (
400 _midnam_model_selector.get_active_text(), mode);
404 MidiTimeAxisView::midi_view()
406 return dynamic_cast<MidiStreamView*>(_view);
410 MidiTimeAxisView::set_height (uint32_t h)
412 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
413 _midi_controls_box.show ();
415 _midi_controls_box.hide();
418 if (h >= KEYBOARD_MIN_HEIGHT) {
419 if (is_track() && _range_scroomer) {
420 _range_scroomer->show();
422 if (is_track() && _piano_roll_header) {
423 _piano_roll_header->show();
426 if (is_track() && _range_scroomer) {
427 _range_scroomer->hide();
429 if (is_track() && _piano_roll_header) {
430 _piano_roll_header->hide();
434 /* We need to do this after changing visibility of our stuff, as it will
435 eventually trigger a call to Editor::reset_controls_layout_width(),
436 which needs to know if we have just shown or hidden a scroomer /
439 RouteTimeAxisView::set_height (h);
443 MidiTimeAxisView::append_extra_display_menu_items ()
445 using namespace Menu_Helpers;
447 MenuList& items = display_menu->items();
450 Menu *range_menu = manage(new Menu);
451 MenuList& range_items = range_menu->items();
452 range_menu->set_name ("ArdourContextMenu");
454 range_items.push_back (
455 MenuElem (_("Show Full Range"),
456 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
457 MidiStreamView::FullRange, true)));
459 range_items.push_back (
460 MenuElem (_("Fit Contents"),
461 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
462 MidiStreamView::ContentsRange, true)));
464 items.push_back (MenuElem (_("Note Range"), *range_menu));
465 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
467 items.push_back (SeparatorElem ());
471 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
473 using namespace Menu_Helpers;
475 /* If we have a controller menu, we need to detach it before
476 RouteTimeAxis::build_automation_action_menu destroys the
477 menu it is attached to. Otherwise GTK destroys
478 controller_menu's gobj, meaning that it can't be reattached
479 below. See bug #3134.
482 if (controller_menu) {
483 detach_menu (*controller_menu);
486 _channel_command_menu_map.clear ();
487 RouteTimeAxisView::build_automation_action_menu (for_selection);
489 MenuList& automation_items = automation_action_menu->items();
491 uint16_t selected_channels = _channel_selector.get_selected_channels();
493 if (selected_channels != 0) {
495 automation_items.push_back (SeparatorElem());
497 /* these 2 MIDI "command" types are semantically more like automation
498 than note data, but they are not MIDI controllers. We give them
499 special status in this menu, since they will not show up in the
500 controller list and anyone who actually knows something about MIDI
501 (!) would not expect to find them there.
504 add_channel_command_menu_item (
505 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
506 automation_items.back().set_sensitive (
507 !for_selection || _editor.get_selection().tracks.size() == 1);
508 add_channel_command_menu_item (
509 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
510 automation_items.back().set_sensitive (
511 !for_selection || _editor.get_selection().tracks.size() == 1);
513 /* now all MIDI controllers. Always offer the possibility that we will
514 rebuild the controllers menu since it might need to be updated after
515 a channel mode change or other change. Also detach it first in case
516 it has been used anywhere else.
519 build_controller_menu ();
521 automation_items.push_back (SeparatorElem());
522 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
523 automation_items.back().set_sensitive (
524 !for_selection || _editor.get_selection().tracks.size() == 1);
526 automation_items.push_back (
527 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
528 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
533 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
535 const uint16_t selected_channels = _channel_selector.get_selected_channels();
537 for (uint8_t chn = 0; chn < 16; chn++) {
538 if (selected_channels & (0x0001 << chn)) {
540 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
541 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
544 menu->set_active (yn);
551 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
553 AutomationType auto_type,
556 using namespace Menu_Helpers;
558 /* count the number of selected channels because we will build a different menu
559 structure if there is more than 1 selected.
562 const uint16_t selected_channels = _channel_selector.get_selected_channels();
565 for (uint8_t chn = 0; chn < 16; chn++) {
566 if (selected_channels & (0x0001 << chn)) {
575 /* multiple channels - create a submenu, with 1 item per channel */
577 Menu* chn_menu = manage (new Menu);
578 MenuList& chn_items (chn_menu->items());
579 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
581 /* add a couple of items to hide/show all of them */
583 chn_items.push_back (
584 MenuElem (_("Hide all channels"),
585 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
586 false, param_without_channel)));
587 chn_items.push_back (
588 MenuElem (_("Show all channels"),
589 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
590 true, param_without_channel)));
592 for (uint8_t chn = 0; chn < 16; chn++) {
593 if (selected_channels & (0x0001 << chn)) {
595 /* for each selected channel, add a menu item for this controller */
597 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
598 chn_items.push_back (
599 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
600 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
601 fully_qualified_param)));
603 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
604 bool visible = false;
607 if (track->marked_for_display()) {
612 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
613 _channel_command_menu_map[fully_qualified_param] = cmi;
614 cmi->set_active (visible);
618 /* now create an item in the parent menu that has the per-channel list as a submenu */
620 items.push_back (MenuElem (label, *chn_menu));
624 /* just one channel - create a single menu item for this command+channel combination*/
626 for (uint8_t chn = 0; chn < 16; chn++) {
627 if (selected_channels & (0x0001 << chn)) {
629 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
631 CheckMenuElem (label,
632 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
633 fully_qualified_param)));
635 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
636 bool visible = false;
639 if (track->marked_for_display()) {
644 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
645 _channel_command_menu_map[fully_qualified_param] = cmi;
646 cmi->set_active (visible);
648 /* one channel only */
655 /** Add a single menu item for a controller on one channel. */
657 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
659 const std::string& name)
661 using namespace Menu_Helpers;
663 const uint16_t selected_channels = _channel_selector.get_selected_channels();
664 for (uint8_t chn = 0; chn < 16; chn++) {
665 if (selected_channels & (0x0001 << chn)) {
667 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
668 ctl_items.push_back (
670 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
672 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
673 fully_qualified_param)));
674 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
676 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
677 fully_qualified_param);
679 bool visible = false;
681 if (track->marked_for_display()) {
686 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
687 _controller_menu_map[fully_qualified_param] = cmi;
688 cmi->set_active (visible);
690 /* one channel only */
696 /** Add a submenu with 1 item per channel for a controller on many channels. */
698 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
700 const std::string& name)
702 using namespace Menu_Helpers;
704 const uint16_t selected_channels = _channel_selector.get_selected_channels();
706 Menu* chn_menu = manage (new Menu);
707 MenuList& chn_items (chn_menu->items());
709 /* add a couple of items to hide/show this controller on all channels */
711 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
712 chn_items.push_back (
713 MenuElem (_("Hide all channels"),
714 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
715 false, param_without_channel)));
716 chn_items.push_back (
717 MenuElem (_("Show all channels"),
718 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
719 true, param_without_channel)));
721 for (uint8_t chn = 0; chn < 16; chn++) {
722 if (selected_channels & (0x0001 << chn)) {
724 /* for each selected channel, add a menu item for this controller */
726 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
727 chn_items.push_back (
728 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
729 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
730 fully_qualified_param)));
732 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
733 fully_qualified_param);
734 bool visible = false;
737 if (track->marked_for_display()) {
742 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
743 _controller_menu_map[fully_qualified_param] = cmi;
744 cmi->set_active (visible);
748 /* add the per-channel menu to the list of controllers, with the name of the controller */
749 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
751 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
755 MidiTimeAxisView::build_controller_menu ()
757 using namespace Menu_Helpers;
759 if (controller_menu) {
760 /* it exists and has not been invalidated by a channel mode change */
764 controller_menu = new Menu; // explicitly managed by us
765 MenuList& items (controller_menu->items());
767 /* create several "top level" menu items for sets of controllers (16 at a
768 time), and populate each one with a submenu for each controller+channel
769 combination covering the currently selected channels for this track
772 const uint16_t selected_channels = _channel_selector.get_selected_channels();
774 /* count the number of selected channels because we will build a different menu
775 structure if there is more than 1 selected.
779 for (uint8_t chn = 0; chn < 16; chn++) {
780 if (selected_channels & (0x0001 << chn)) {
787 using namespace MIDI::Name;
788 const Glib::ustring model = _midnam_model_selector.get_active_text();
789 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
790 .document_by_model(model);
791 boost::shared_ptr<MasterDeviceNames> device_names;
794 device_names = midnam->master_device_names(model);
797 if (device_names && !device_names->controls().empty()) {
798 /* Controllers names available in midnam file, generate fancy menu */
799 unsigned n_items = 0;
800 unsigned n_groups = 0;
801 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
802 l != device_names->controls().end(); ++l) {
803 boost::shared_ptr<ControlNameList> name_list = *l;
804 Menu* ctl_menu = NULL;
806 for (ControlNameList::Controls::const_iterator c = (*l)->controls().begin();
807 c != (*l)->controls().end(); ++c) {
808 const int ctl = atoi((*c)->number().c_str());
809 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
810 /* Skip bank select controllers since they're handled specially */
815 /* Create a new submenu */
816 ctl_menu = manage (new Menu);
819 MenuList& ctl_items (ctl_menu->items());
821 add_multi_channel_controller_item(ctl_items, ctl, (*c)->name());
823 add_single_channel_controller_item(ctl_items, ctl, (*c)->name());
826 if (++n_items == 16 || c == (*l)->controls().end()) {
827 /* Submenu has 16 items, add it to controller menu and reset */
829 MenuElem(string_compose(_("Controllers %1-%2"),
830 (16 * n_groups), (16 * n_groups) + n_items - 1),
839 /* No controllers names, generate generic numeric menu */
840 for (int i = 0; i < 127; i += 16) {
841 Menu* ctl_menu = manage (new Menu);
842 MenuList& ctl_items (ctl_menu->items());
844 for (int ctl = i; ctl < i+16; ++ctl) {
845 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
846 /* Skip bank select controllers since they're handled specially */
851 add_multi_channel_controller_item(
852 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
854 add_single_channel_controller_item(
855 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
859 /* Add submenu for this block of controllers to controller menu */
861 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
868 MidiTimeAxisView::build_note_mode_menu()
870 using namespace Menu_Helpers;
872 Menu* mode_menu = manage (new Menu);
873 MenuList& items = mode_menu->items();
874 mode_menu->set_name ("ArdourContextMenu");
876 RadioMenuItem::Group mode_group;
878 RadioMenuElem (mode_group,_("Sustained"),
879 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
881 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
882 _note_mode_item->set_active(_note_mode == Sustained);
885 RadioMenuElem (mode_group, _("Percussive"),
886 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
888 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
889 _percussion_mode_item->set_active(_note_mode == Percussive);
895 MidiTimeAxisView::build_color_mode_menu()
897 using namespace Menu_Helpers;
899 Menu* mode_menu = manage (new Menu);
900 MenuList& items = mode_menu->items();
901 mode_menu->set_name ("ArdourContextMenu");
903 RadioMenuItem::Group mode_group;
905 RadioMenuElem (mode_group, _("Meter Colors"),
906 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
907 MeterColors, false, true, true)));
908 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
909 _meter_color_mode_item->set_active(_color_mode == MeterColors);
912 RadioMenuElem (mode_group, _("Channel Colors"),
913 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
914 ChannelColors, false, true, true)));
915 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
916 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
919 RadioMenuElem (mode_group, _("Track Color"),
920 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
921 TrackColor, false, true, true)));
922 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
923 _channel_color_mode_item->set_active(_color_mode == TrackColor);
929 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
931 if (apply_to_selection) {
932 _editor.get_selection().tracks.foreach_midi_time_axis (
933 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
935 if (_note_mode != mode || midi_track()->note_mode() != mode) {
937 midi_track()->set_note_mode(mode);
938 set_gui_property ("note-mode", enum_2_string(_note_mode));
939 _view->redisplay_track();
945 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
947 if (apply_to_selection) {
948 _editor.get_selection().tracks.foreach_midi_time_axis (
949 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
951 if (_color_mode == mode && !force) {
955 if (mode == ChannelColors) {
956 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
958 _channel_selector.set_default_channel_color();
962 set_gui_property ("color-mode", enum_2_string(_color_mode));
964 _view->redisplay_track();
970 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
972 if (apply_to_selection) {
973 _editor.get_selection().tracks.foreach_midi_time_axis (
974 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
976 if (!_ignore_signals) {
977 midi_view()->set_note_range(range);
983 MidiTimeAxisView::update_range()
985 MidiGhostRegion* mgr;
987 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
988 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
995 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
997 if (apply_to_selection) {
998 _editor.get_selection().tracks.foreach_midi_time_axis (
999 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1002 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1004 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1005 create_automation_child(*i, true);
1009 RouteTimeAxisView::show_all_automation ();
1014 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1016 if (apply_to_selection) {
1017 _editor.get_selection().tracks.foreach_midi_time_axis (
1018 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1021 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1023 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1024 create_automation_child (*i, true);
1028 RouteTimeAxisView::show_existing_automation ();
1032 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1035 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1037 if (param.type() == NullAutomation) {
1041 AutomationTracks::iterator existing = _automation_tracks.find (param);
1043 if (existing != _automation_tracks.end()) {
1045 /* automation track created because we had existing data for
1046 * the processor, but visibility may need to be controlled
1047 * since it will have been set visible by default.
1050 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1057 boost::shared_ptr<AutomationTimeAxisView> track;
1059 switch (param.type()) {
1061 case GainAutomation:
1062 create_gain_automation_child (param, show);
1065 case PluginAutomation:
1066 /* handled elsewhere */
1069 case MidiCCAutomation:
1070 case MidiPgmChangeAutomation:
1071 case MidiPitchBenderAutomation:
1072 case MidiChannelPressureAutomation:
1073 case MidiSystemExclusiveAutomation:
1074 /* These controllers are region "automation" - they are owned
1075 * by regions (and their MidiModels), not by the track. As a
1076 * result we do not create an AutomationList/Line for the track
1077 * ... except here we are doing something!! XXX
1080 track.reset (new AutomationTimeAxisView (
1083 boost::shared_ptr<Automatable> (),
1084 boost::shared_ptr<AutomationControl> (),
1090 _route->describe_parameter(param)));
1093 _view->foreach_regionview (
1094 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1097 add_automation_child (param, track, show);
1101 error << "MidiTimeAxisView: unknown automation child "
1102 << EventTypeMap::instance().to_symbol(param) << endmsg;
1107 MidiTimeAxisView::route_active_changed ()
1109 RouteUI::route_active_changed ();
1112 if (_route->active()) {
1113 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1114 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1115 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1117 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1118 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1119 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1122 if (_route->active()) {
1123 controls_ebox.set_name ("BusControlsBaseUnselected");
1124 controls_base_selected_name = "BusControlsBaseSelected";
1125 controls_base_unselected_name = "BusControlsBaseUnselected";
1127 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1128 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1129 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1135 MidiTimeAxisView::set_note_selection (uint8_t note)
1137 if (!_editor.internal_editing()) {
1141 uint16_t chn_mask = _channel_selector.get_selected_channels();
1143 if (_view->num_selected_regionviews() == 0) {
1144 _view->foreach_regionview (
1145 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1148 _view->foreach_selected_regionview (
1149 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1155 MidiTimeAxisView::add_note_selection (uint8_t note)
1157 if (!_editor.internal_editing()) {
1161 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1163 if (_view->num_selected_regionviews() == 0) {
1164 _view->foreach_regionview (
1165 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1168 _view->foreach_selected_regionview (
1169 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1175 MidiTimeAxisView::extend_note_selection (uint8_t note)
1177 if (!_editor.internal_editing()) {
1181 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1183 if (_view->num_selected_regionviews() == 0) {
1184 _view->foreach_regionview (
1185 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1188 _view->foreach_selected_regionview (
1189 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1195 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1197 if (!_editor.internal_editing()) {
1201 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1203 if (_view->num_selected_regionviews() == 0) {
1204 _view->foreach_regionview (
1205 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1208 _view->foreach_selected_regionview (
1209 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1215 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1217 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1221 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1223 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1227 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1229 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1233 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1235 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1239 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1241 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1245 const uint16_t selected_channels = _channel_selector.get_selected_channels();
1246 bool changed = false;
1250 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1252 for (uint32_t chn = 0; chn < 16; ++chn) {
1253 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1254 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1260 if ((selected_channels & (0x0001 << chn)) == 0) {
1261 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1262 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1264 changed = track->set_marked_for_display (false) || changed;
1266 changed = track->set_marked_for_display (true) || changed;
1273 /* TODO: Bender, Pressure */
1275 /* invalidate the controller menu, so that we rebuild it next time */
1276 _controller_menu_map.clear ();
1277 delete controller_menu;
1278 controller_menu = 0;
1286 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1288 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1293 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1294 if (i != _controller_menu_map.end()) {
1298 i = _channel_command_menu_map.find (param);
1299 if (i != _channel_command_menu_map.end()) {
1306 boost::shared_ptr<MidiRegion>
1307 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1309 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1311 real_editor->begin_reversible_command (Operations::create_region);
1312 playlist()->clear_changes ();
1314 real_editor->snap_to (pos, 0);
1316 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1317 view()->trackview().track().get(), view()->trackview().track()->name());
1320 plist.add (ARDOUR::Properties::start, 0);
1321 plist.add (ARDOUR::Properties::length, length);
1322 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1324 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1326 playlist()->add_region (region, pos);
1327 _session->add_command (new StatefulDiffCommand (playlist()));
1330 real_editor->commit_reversible_command ();
1333 return boost::dynamic_pointer_cast<MidiRegion>(region);
1337 MidiTimeAxisView::ensure_step_editor ()
1339 if (!_step_editor) {
1340 _step_editor = new StepEditor (_editor, midi_track(), *this);
1345 MidiTimeAxisView::start_step_editing ()
1347 ensure_step_editor ();
1348 _step_editor->start_step_editing ();
1352 MidiTimeAxisView::stop_step_editing ()
1355 _step_editor->stop_step_editing ();
1360 /** @return channel (counted from 0) to add an event to, based on the current setting
1361 * of the channel selector.
1364 MidiTimeAxisView::get_channel_for_add () const
1366 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1368 uint8_t channel = 0;
1370 /* pick the highest selected channel, unless all channels are selected,
1371 which is interpreted to mean channel 1 (zero)
1374 for (uint16_t i = 0; i < 16; ++i) {
1375 if (chn_mask & (1<<i)) {
1381 if (chn_cnt == 16) {
1389 MidiTimeAxisView::note_range_changed ()
1391 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1392 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1396 MidiTimeAxisView::contents_height_changed ()
1398 _range_scroomer->set_size_request (-1, _view->child_height ());