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 "automation_line.h"
63 #include "automation_time_axis.h"
64 #include "canvas-note-event.h"
65 #include "canvas_impl.h"
66 #include "crossfade_view.h"
69 #include "ghostregion.h"
70 #include "gui_thread.h"
72 #include "midi_scroomer.h"
73 #include "midi_streamview.h"
74 #include "midi_region_view.h"
75 #include "midi_time_axis.h"
76 #include "piano_roll_header.h"
77 #include "playlist_selector.h"
78 #include "plugin_selector.h"
79 #include "plugin_ui.h"
80 #include "point_selection.h"
82 #include "region_view.h"
83 #include "rgb_macros.h"
84 #include "selection.h"
85 #include "step_editor.h"
86 #include "simplerect.h"
89 #include "ardour/midi_track.h"
93 using namespace ARDOUR;
96 using namespace Gtkmm2ext;
97 using namespace Editing;
99 // Minimum height at which a control is displayed
100 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
101 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
103 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
104 : AxisView(sess) // virtually inherited
105 , RouteTimeAxisView(ed, sess, canvas)
106 , _ignore_signals(false)
108 , _piano_roll_header(0)
109 , _note_mode(Sustained)
111 , _percussion_mode_item(0)
112 , _color_mode(MeterColors)
113 , _meter_color_mode_item(0)
114 , _channel_color_mode_item(0)
115 , _track_color_mode_item(0)
116 , _step_edit_item (0)
117 , _midi_thru_item (0)
118 , controller_menu (0)
124 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
126 RouteTimeAxisView::set_route (rt);
128 subplugin_menu.set_name ("ArdourContextMenu");
130 _view = new MidiStreamView (*this);
131 if (!gui_property ("note-range-min").empty ()) {
132 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
134 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
136 ignore_toggle = false;
138 mute_button->set_active (false);
139 solo_button->set_active (false);
141 if (is_midi_track()) {
142 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
143 _note_mode = midi_track()->note_mode();
144 } else { // MIDI bus (which doesn't exist yet..)
145 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
148 /* map current state of the route */
150 processors_changed (RouteProcessorChange ());
152 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
155 _piano_roll_header = new PianoRollHeader(*midi_view());
157 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
158 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
159 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
161 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
163 /* Suspend updates of the StreamView during scroomer drags to speed things up */
164 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
165 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
167 controls_hbox.pack_start(*_range_scroomer);
168 controls_hbox.pack_start(*_piano_roll_header);
170 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
171 controls_base_selected_name = "MidiTrackControlsBaseSelected";
172 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
174 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
176 /* ask for notifications of any new RegionViews */
177 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
179 if (!_editor.have_idled()) {
180 /* first idle will do what we need */
186 HBox* midi_controls_hbox = manage(new HBox());
188 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
190 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
191 for (; m != patch_manager.all_models().end(); ++m) {
192 _model_selector.append_text(m->c_str());
195 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
197 _custom_device_mode_selector.signal_changed().connect(
198 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
200 // TODO: persist the choice
201 // this initializes the comboboxes and sends out the signal
202 _model_selector.set_active(0);
204 midi_controls_hbox->pack_start(_channel_selector, true, false);
205 if (!patch_manager.all_models().empty()) {
206 _midi_controls_box.pack_start(_model_selector, true, false);
207 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
210 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
212 controls_vbox.pack_start(_midi_controls_box, false, false);
214 // restore channel selector settings
215 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
216 _channel_selector.mode_changed.connect(
217 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
218 _channel_selector.mode_changed.connect(
219 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
221 string prop = gui_property ("color-mode");
223 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
224 if (_color_mode == ChannelColors) {
225 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
229 set_color_mode (_color_mode, true, false);
231 prop = gui_property ("note-mode");
233 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
234 if (_percussion_mode_item) {
235 _percussion_mode_item->set_active (_note_mode == Percussive);
239 /* Look for any GUI object state nodes that represent automation children that should exist, and create
243 GUIObjectState& gui_state = gui_object_state ();
244 for (GUIObjectState::StringPropertyMap::const_iterator i = gui_state.begin(); i != gui_state.end(); ++i) {
247 Evoral::Parameter parameter (0, 0, 0);
249 bool const p = AutomationTimeAxisView::parse_state_id (i->first, route_id, has_parameter, parameter);
250 if (p && route_id == _route->id () && has_parameter) {
251 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (i->first, X_("visible"))));
257 MidiTimeAxisView::first_idle ()
264 MidiTimeAxisView::~MidiTimeAxisView ()
266 delete _piano_roll_header;
267 _piano_roll_header = 0;
269 delete _range_scroomer;
272 delete controller_menu;
277 MidiTimeAxisView::enter_internal_edit_mode ()
280 midi_view()->enter_internal_edit_mode ();
285 MidiTimeAxisView::leave_internal_edit_mode ()
288 midi_view()->leave_internal_edit_mode ();
293 MidiTimeAxisView::check_step_edit ()
295 ensure_step_editor ();
296 _step_editor->check_step_edit ();
300 MidiTimeAxisView::model_changed()
302 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
303 .custom_device_mode_names_by_model(_model_selector.get_active_text());
305 _custom_device_mode_selector.clear_items();
307 for (std::list<std::string>::const_iterator i = device_modes.begin();
308 i != device_modes.end(); ++i) {
309 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
310 _custom_device_mode_selector.append_text(*i);
313 _custom_device_mode_selector.set_active(0);
316 void MidiTimeAxisView::custom_device_mode_changed()
318 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
319 _custom_device_mode_selector.get_active_text());
323 MidiTimeAxisView::midi_view()
325 return dynamic_cast<MidiStreamView*>(_view);
329 MidiTimeAxisView::set_height (uint32_t h)
331 RouteTimeAxisView::set_height (h);
333 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
334 _midi_controls_box.show_all ();
336 _midi_controls_box.hide();
339 if (height >= KEYBOARD_MIN_HEIGHT) {
340 if (is_track() && _range_scroomer) {
341 _range_scroomer->show();
343 if (is_track() && _piano_roll_header) {
344 _piano_roll_header->show();
347 if (is_track() && _range_scroomer) {
348 _range_scroomer->hide();
350 if (is_track() && _piano_roll_header) {
351 _piano_roll_header->hide();
357 MidiTimeAxisView::append_extra_display_menu_items ()
359 using namespace Menu_Helpers;
361 MenuList& items = display_menu->items();
364 Menu *range_menu = manage(new Menu);
365 MenuList& range_items = range_menu->items();
366 range_menu->set_name ("ArdourContextMenu");
368 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
369 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
370 MidiStreamView::FullRange)));
372 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
373 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
374 MidiStreamView::ContentsRange)));
376 items.push_back (MenuElem (_("Note Range"), *range_menu));
377 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
379 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
380 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
382 items.push_back (SeparatorElem ());
386 MidiTimeAxisView::toggle_midi_thru ()
388 if (!_midi_thru_item) {
392 bool view_yn = _midi_thru_item->get_active();
393 if (view_yn != midi_track()->midi_thru()) {
394 midi_track()->set_midi_thru (view_yn);
399 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
401 using namespace Menu_Helpers;
403 /* If we have a controller menu, we need to detach it before
404 RouteTimeAxis::build_automation_action_menu destroys the
405 menu it is attached to. Otherwise GTK destroys
406 controller_menu's gobj, meaning that it can't be reattached
407 below. See bug #3134.
410 if (controller_menu) {
411 detach_menu (*controller_menu);
414 _channel_command_menu_map.clear ();
415 RouteTimeAxisView::build_automation_action_menu (for_selection);
417 MenuList& automation_items = automation_action_menu->items();
419 uint16_t selected_channels = _channel_selector.get_selected_channels();
421 if (selected_channels != 0) {
423 automation_items.push_back (SeparatorElem());
425 /* these 2 MIDI "command" types are semantically more like automation than note data,
426 but they are not MIDI controllers. We give them special status in this menu, since
427 they will not show up in the controller list and anyone who actually knows
428 something about MIDI (!) would not expect to find them there.
431 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
432 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
433 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
434 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
436 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
437 since it might need to be updated after a channel mode change or other change. Also detach it
438 first in case it has been used anywhere else.
441 build_controller_menu ();
443 automation_items.push_back (SeparatorElem());
444 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
445 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
447 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
448 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
454 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
456 uint16_t selected_channels = _channel_selector.get_selected_channels();
458 for (uint8_t chn = 0; chn < 16; chn++) {
459 if (selected_channels & (0x0001 << chn)) {
461 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
462 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
465 menu->set_active (yn);
472 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
474 using namespace Menu_Helpers;
476 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
479 uint16_t selected_channels = _channel_selector.get_selected_channels();
482 for (uint8_t chn = 0; chn < 16; chn++) {
483 if (selected_channels & (0x0001 << chn)) {
492 /* multiple channels - create a submenu, with 1 item per channel */
494 Menu* chn_menu = manage (new Menu);
495 MenuList& chn_items (chn_menu->items());
496 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
498 /* add a couple of items to hide/show all of them */
500 chn_items.push_back (MenuElem (_("Hide all channels"),
501 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
502 false, param_without_channel)));
503 chn_items.push_back (MenuElem (_("Show all channels"),
504 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
505 true, param_without_channel)));
507 for (uint8_t chn = 0; chn < 16; chn++) {
508 if (selected_channels & (0x0001 << chn)) {
510 /* for each selected channel, add a menu item for this controller */
512 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
513 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
514 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
515 fully_qualified_param)));
517 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
518 bool visible = false;
521 if (track->marked_for_display()) {
526 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
527 _channel_command_menu_map[fully_qualified_param] = cmi;
528 cmi->set_active (visible);
532 /* now create an item in the parent menu that has the per-channel list as a submenu */
534 items.push_back (MenuElem (label, *chn_menu));
538 /* just one channel - create a single menu item for this command+channel combination*/
540 for (uint8_t chn = 0; chn < 16; chn++) {
541 if (selected_channels & (0x0001 << chn)) {
543 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
544 items.push_back (CheckMenuElem (label,
545 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
546 fully_qualified_param)));
548 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
549 bool visible = false;
552 if (track->marked_for_display()) {
557 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
558 _channel_command_menu_map[fully_qualified_param] = cmi;
559 cmi->set_active (visible);
561 /* one channel only */
569 MidiTimeAxisView::build_controller_menu ()
571 using namespace Menu_Helpers;
573 if (controller_menu) {
574 /* it exists and has not been invalidated by a channel mode change, so just return it */
578 controller_menu = new Menu; // explicitly managed by us
579 MenuList& items (controller_menu->items());
581 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
582 for each controller+channel combination covering the currently selected channels for this track
585 uint16_t selected_channels = _channel_selector.get_selected_channels();
587 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
592 for (uint8_t chn = 0; chn < 16; chn++) {
593 if (selected_channels & (0x0001 << chn)) {
600 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
601 bank select controllers, as they are handled by the `patch' code */
603 for (int i = 0; i < 127; i += 16) {
605 Menu* ctl_menu = manage (new Menu);
606 MenuList& ctl_items (ctl_menu->items());
609 /* for each controller, consider whether to create a submenu or a single item */
611 for (int ctl = i; ctl < i+16; ++ctl) {
613 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
619 /* multiple channels - create a submenu, with 1 item per channel */
621 Menu* chn_menu = manage (new Menu);
622 MenuList& chn_items (chn_menu->items());
624 /* add a couple of items to hide/show this controller on all channels */
626 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
627 chn_items.push_back (MenuElem (_("Hide all channels"),
628 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
629 false, param_without_channel)));
630 chn_items.push_back (MenuElem (_("Show all channels"),
631 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
632 true, param_without_channel)));
634 for (uint8_t chn = 0; chn < 16; chn++) {
635 if (selected_channels & (0x0001 << chn)) {
637 /* for each selected channel, add a menu item for this controller */
639 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
640 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
641 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
642 fully_qualified_param)));
644 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
645 bool visible = false;
648 if (track->marked_for_display()) {
653 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
654 _controller_menu_map[fully_qualified_param] = cmi;
655 cmi->set_active (visible);
659 /* add the per-channel menu to the list of controllers, with the name of the controller */
660 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
661 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
665 /* just one channel - create a single menu item for this ctl+channel combination*/
667 for (uint8_t chn = 0; chn < 16; chn++) {
668 if (selected_channels & (0x0001 << chn)) {
670 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
671 ctl_items.push_back (
673 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
674 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
675 fully_qualified_param)
678 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
680 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
681 bool visible = false;
684 if (track->marked_for_display()) {
689 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
690 _controller_menu_map[fully_qualified_param] = cmi;
691 cmi->set_active (visible);
693 /* one channel only */
700 /* add the menu for this block of controllers to the overall controller menu */
702 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
707 MidiTimeAxisView::build_note_mode_menu()
709 using namespace Menu_Helpers;
711 Menu* mode_menu = manage (new Menu);
712 MenuList& items = mode_menu->items();
713 mode_menu->set_name ("ArdourContextMenu");
715 RadioMenuItem::Group mode_group;
716 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
717 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
718 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
719 _note_mode_item->set_active(_note_mode == Sustained);
721 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
722 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
723 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
724 _percussion_mode_item->set_active(_note_mode == Percussive);
730 MidiTimeAxisView::build_color_mode_menu()
732 using namespace Menu_Helpers;
734 Menu* mode_menu = manage (new Menu);
735 MenuList& items = mode_menu->items();
736 mode_menu->set_name ("ArdourContextMenu");
738 RadioMenuItem::Group mode_group;
739 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
740 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
741 MeterColors, false, true)));
742 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
743 _meter_color_mode_item->set_active(_color_mode == MeterColors);
745 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
746 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
747 ChannelColors, false, true)));
748 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
749 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
751 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
752 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
753 TrackColor, false, true)));
754 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
755 _channel_color_mode_item->set_active(_color_mode == TrackColor);
761 MidiTimeAxisView::set_note_mode(NoteMode mode)
763 if (_note_mode != mode || midi_track()->note_mode() != mode) {
765 midi_track()->set_note_mode(mode);
766 set_gui_property ("note-mode", enum_2_string(_note_mode));
767 _view->redisplay_track();
772 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
774 if (_color_mode == mode && !force) {
778 if (mode == ChannelColors) {
779 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
781 _channel_selector.set_default_channel_color();
785 set_gui_property ("color-mode", enum_2_string(_color_mode));
787 _view->redisplay_track();
792 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
794 if (!_ignore_signals)
795 midi_view()->set_note_range(range);
800 MidiTimeAxisView::update_range()
802 MidiGhostRegion* mgr;
804 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
805 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
812 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
814 if (apply_to_selection) {
815 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
818 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
820 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
821 create_automation_child(*i, true);
825 RouteTimeAxisView::show_all_automation ();
830 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
832 if (apply_to_selection) {
833 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
836 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
838 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
839 create_automation_child (*i, true);
843 RouteTimeAxisView::show_existing_automation ();
847 /** Create an automation track for the given parameter (pitch bend, channel pressure).
850 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
852 if (param.type() == NullAutomation) {
853 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
857 AutomationTracks::iterator existing = _automation_tracks.find (param);
859 if (existing != _automation_tracks.end()) {
861 /* automation track created because we had existing data for
862 * the processor, but visibility may need to be controlled
863 * since it will have been set visible by default.
866 cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
868 if (existing->second->set_marked_for_display (show) && !no_redraw) {
875 boost::shared_ptr<AutomationTimeAxisView> track;
877 switch (param.type()) {
880 create_gain_automation_child (param, show);
883 case PluginAutomation:
884 /* handled elsewhere */
887 case MidiCCAutomation:
888 case MidiPgmChangeAutomation:
889 case MidiPitchBenderAutomation:
890 case MidiChannelPressureAutomation:
891 case MidiSystemExclusiveAutomation:
892 /* These controllers are region "automation" - they are owned
893 * by regions (and their MidiModels), not by the track. As a
894 * result we do not create an AutomationList/Line for the track
895 * ... except here we are doing something!! XXX
898 track.reset (new AutomationTimeAxisView (
901 boost::shared_ptr<Automatable> (),
902 boost::shared_ptr<AutomationControl> (),
908 _route->describe_parameter(param)
912 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
915 add_automation_child (param, track, show);
919 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
924 MidiTimeAxisView::route_active_changed ()
926 RouteUI::route_active_changed ();
929 if (_route->active()) {
930 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
931 controls_base_selected_name = "MidiTrackControlsBaseSelected";
932 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
934 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
935 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
936 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
942 if (_route->active()) {
943 controls_ebox.set_name ("BusControlsBaseUnselected");
944 controls_base_selected_name = "BusControlsBaseSelected";
945 controls_base_unselected_name = "BusControlsBaseUnselected";
947 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
948 controls_base_selected_name = "BusControlsBaseInactiveSelected";
949 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
957 MidiTimeAxisView::add_note_selection (uint8_t note)
959 if (!_editor.internal_editing()) {
963 uint16_t chn_mask = _channel_selector.get_selected_channels();
965 if (_view->num_selected_regionviews() == 0) {
966 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
968 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
973 MidiTimeAxisView::extend_note_selection (uint8_t note)
975 if (!_editor.internal_editing()) {
979 uint16_t chn_mask = _channel_selector.get_selected_channels();
981 if (_view->num_selected_regionviews() == 0) {
982 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
984 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
989 MidiTimeAxisView::toggle_note_selection (uint8_t note)
991 if (!_editor.internal_editing()) {
995 uint16_t chn_mask = _channel_selector.get_selected_channels();
997 if (_view->num_selected_regionviews() == 0) {
998 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1000 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1005 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1007 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1011 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1013 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1017 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1019 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1023 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1025 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1029 uint16_t selected_channels = _channel_selector.get_selected_channels();
1030 bool changed = false;
1034 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1036 for (uint32_t chn = 0; chn < 16; ++chn) {
1037 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1038 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1044 if ((selected_channels & (0x0001 << chn)) == 0) {
1045 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1046 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1048 changed = track->set_marked_for_display (false) || changed;
1050 changed = track->set_marked_for_display (true) || changed;
1057 /* TODO: Bender, Pressure */
1059 /* invalidate the controller menu, so that we rebuild it next time */
1060 _controller_menu_map.clear ();
1061 delete controller_menu;
1062 controller_menu = 0;
1070 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1072 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1077 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1078 if (i != _controller_menu_map.end()) {
1082 i = _channel_command_menu_map.find (param);
1083 if (i != _channel_command_menu_map.end()) {
1090 boost::shared_ptr<MidiRegion>
1091 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1093 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1095 real_editor->begin_reversible_command (Operations::create_region);
1096 playlist()->clear_changes ();
1098 real_editor->snap_to (pos, 0);
1100 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1101 view()->trackview().track()->name());
1104 plist.add (ARDOUR::Properties::start, 0);
1105 plist.add (ARDOUR::Properties::length, length);
1106 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1108 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1110 playlist()->add_region (region, pos);
1111 _session->add_command (new StatefulDiffCommand (playlist()));
1114 real_editor->commit_reversible_command ();
1117 return boost::dynamic_pointer_cast<MidiRegion>(region);
1121 MidiTimeAxisView::ensure_step_editor ()
1123 if (!_step_editor) {
1124 _step_editor = new StepEditor (_editor, midi_track(), *this);
1129 MidiTimeAxisView::start_step_editing ()
1131 ensure_step_editor ();
1132 _step_editor->start_step_editing ();
1136 MidiTimeAxisView::stop_step_editing ()
1139 _step_editor->stop_step_editing ();
1144 /** @return channel (counted from 0) to add an event to, based on the current setting
1145 * of the channel selector.
1148 MidiTimeAxisView::get_channel_for_add () const
1150 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1152 uint8_t channel = 0;
1154 /* pick the highest selected channel, unless all channels are selected,
1155 which is interpreted to mean channel 1 (zero)
1158 for (uint16_t i = 0; i < 16; ++i) {
1159 if (chn_mask & (1<<i)) {
1165 if (chn_cnt == 16) {
1173 MidiTimeAxisView::note_range_changed ()
1175 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1176 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());