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/midi_playlist.h"
43 #include "ardour/midi_diskstream.h"
44 #include "ardour/midi_patch_manager.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/processor.h"
47 #include "ardour/ladspa_plugin.h"
48 #include "ardour/location.h"
49 #include "ardour/playlist.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlist.h"
53 #include "ardour/tempo.h"
54 #include "ardour/utils.h"
55 #include "ardour/operations.h"
57 #include "midi++/names.h"
59 #include "add_midi_cc_track_dialog.h"
60 #include "ardour_ui.h"
61 #include "automation_line.h"
62 #include "automation_time_axis.h"
63 #include "canvas-note-event.h"
64 #include "canvas_impl.h"
65 #include "crossfade_view.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,
103 boost::shared_ptr<Route> rt, Canvas& canvas)
104 : AxisView(sess) // virtually inherited
105 , RouteTimeAxisView(ed, sess, rt, 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)
121 subplugin_menu.set_name ("ArdourContextMenu");
123 _view = new MidiStreamView (*this);
125 ignore_toggle = false;
127 mute_button->set_active (false);
128 solo_button->set_active (false);
130 if (is_midi_track()) {
131 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
132 _note_mode = midi_track()->note_mode();
133 } else { // MIDI bus (which doesn't exist yet..)
134 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
137 /* map current state of the route */
139 processors_changed (RouteProcessorChange ());
143 set_state (*xml_node, Stateful::loading_state_version);
145 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
148 _piano_roll_header = new PianoRollHeader(*midi_view());
150 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
151 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
152 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
154 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
156 /* Suspend updates of the StreamView during scroomer drags to speed things up */
157 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
158 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
160 controls_hbox.pack_start(*_range_scroomer);
161 controls_hbox.pack_start(*_piano_roll_header);
163 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
164 controls_base_selected_name = "MidiTrackControlsBaseSelected";
165 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
167 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
169 /* ask for notifications of any new RegionViews */
170 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
172 if (!_editor.have_idled()) {
173 /* first idle will do what we need */
179 HBox* midi_controls_hbox = manage(new HBox());
181 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
183 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
184 for (; m != patch_manager.all_models().end(); ++m) {
185 _model_selector.append_text(m->c_str());
188 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
190 _custom_device_mode_selector.signal_changed().connect(
191 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
193 // TODO: persist the choice
194 // this initializes the comboboxes and sends out the signal
195 _model_selector.set_active(0);
197 midi_controls_hbox->pack_start(_channel_selector, true, false);
198 if (!patch_manager.all_models().empty()) {
199 _midi_controls_box.pack_start(_model_selector, true, false);
200 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
203 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
205 controls_vbox.pack_start(_midi_controls_box, false, false);
207 // restore channel selector settings
208 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
209 _channel_selector.mode_changed.connect(
210 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
211 _channel_selector.mode_changed.connect(
212 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
215 if ((prop = xml_node->property ("color-mode")) != 0) {
216 _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
217 if (_color_mode == ChannelColors) {
218 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
222 if ((prop = xml_node->property ("note-mode")) != 0) {
223 _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
224 if (_percussion_mode_item) {
225 _percussion_mode_item->set_active (_note_mode == Percussive);
229 set_color_mode (_color_mode, true, false);
233 MidiTimeAxisView::first_idle ()
240 MidiTimeAxisView::~MidiTimeAxisView ()
242 delete _piano_roll_header;
243 _piano_roll_header = 0;
245 delete _range_scroomer;
248 delete controller_menu;
253 MidiTimeAxisView::enter_internal_edit_mode ()
256 midi_view()->enter_internal_edit_mode ();
261 MidiTimeAxisView::leave_internal_edit_mode ()
264 midi_view()->leave_internal_edit_mode ();
269 MidiTimeAxisView::check_step_edit ()
271 ensure_step_editor ();
272 _step_editor->check_step_edit ();
276 MidiTimeAxisView::model_changed()
278 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
279 .custom_device_mode_names_by_model(_model_selector.get_active_text());
281 _custom_device_mode_selector.clear_items();
283 for (std::list<std::string>::const_iterator i = device_modes.begin();
284 i != device_modes.end(); ++i) {
285 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
286 _custom_device_mode_selector.append_text(*i);
289 _custom_device_mode_selector.set_active(0);
292 void MidiTimeAxisView::custom_device_mode_changed()
294 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
295 _custom_device_mode_selector.get_active_text());
299 MidiTimeAxisView::midi_view()
301 return dynamic_cast<MidiStreamView*>(_view);
305 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
308 xml_node->add_property ("shown-editor", "yes");
310 guint32 ret = TimeAxisView::show_at (y, nth, parent);
315 MidiTimeAxisView::hide ()
318 xml_node->add_property ("shown-editor", "no");
320 TimeAxisView::hide ();
324 MidiTimeAxisView::set_height (uint32_t h)
326 RouteTimeAxisView::set_height (h);
328 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
329 _midi_controls_box.show_all ();
331 _midi_controls_box.hide();
334 if (height >= KEYBOARD_MIN_HEIGHT) {
335 if (is_track() && _range_scroomer)
336 _range_scroomer->show();
337 if (is_track() && _piano_roll_header)
338 _piano_roll_header->show();
340 if (is_track() && _range_scroomer)
341 _range_scroomer->hide();
342 if (is_track() && _piano_roll_header)
343 _piano_roll_header->hide();
348 MidiTimeAxisView::append_extra_display_menu_items ()
350 using namespace Menu_Helpers;
352 MenuList& items = display_menu->items();
355 Menu *range_menu = manage(new Menu);
356 MenuList& range_items = range_menu->items();
357 range_menu->set_name ("ArdourContextMenu");
359 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
360 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
361 MidiStreamView::FullRange)));
363 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
364 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
365 MidiStreamView::ContentsRange)));
367 items.push_back (MenuElem (_("Note Range"), *range_menu));
368 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
370 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
371 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
373 items.push_back (SeparatorElem ());
377 MidiTimeAxisView::toggle_midi_thru ()
379 if (!_midi_thru_item) {
383 bool view_yn = _midi_thru_item->get_active();
384 if (view_yn != midi_track()->midi_thru()) {
385 midi_track()->set_midi_thru (view_yn);
390 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
392 using namespace Menu_Helpers;
394 /* If we have a controller menu, we need to detach it before
395 RouteTimeAxis::build_automation_action_menu destroys the
396 menu it is attached to. Otherwise GTK destroys
397 controller_menu's gobj, meaning that it can't be reattached
398 below. See bug #3134.
401 if (controller_menu) {
402 detach_menu (*controller_menu);
405 _channel_command_menu_map.clear ();
406 RouteTimeAxisView::build_automation_action_menu (for_selection);
408 MenuList& automation_items = automation_action_menu->items();
410 uint16_t selected_channels = _channel_selector.get_selected_channels();
412 if (selected_channels != 0) {
414 automation_items.push_back (SeparatorElem());
416 /* these 2 MIDI "command" types are semantically more like automation than note data,
417 but they are not MIDI controllers. We give them special status in this menu, since
418 they will not show up in the controller list and anyone who actually knows
419 something about MIDI (!) would not expect to find them there.
422 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
423 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
424 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
425 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
427 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
428 since it might need to be updated after a channel mode change or other change. Also detach it
429 first in case it has been used anywhere else.
432 build_controller_menu ();
434 automation_items.push_back (SeparatorElem());
435 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
436 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
438 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
439 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
445 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
447 uint16_t selected_channels = _channel_selector.get_selected_channels();
449 for (uint8_t chn = 0; chn < 16; chn++) {
450 if (selected_channels & (0x0001 << chn)) {
452 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
453 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
456 menu->set_active (yn);
463 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
465 using namespace Menu_Helpers;
467 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
470 uint16_t selected_channels = _channel_selector.get_selected_channels();
473 for (uint8_t chn = 0; chn < 16; chn++) {
474 if (selected_channels & (0x0001 << chn)) {
483 /* multiple channels - create a submenu, with 1 item per channel */
485 Menu* chn_menu = manage (new Menu);
486 MenuList& chn_items (chn_menu->items());
487 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
489 /* add a couple of items to hide/show all of them */
491 chn_items.push_back (MenuElem (_("Hide all channels"),
492 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
493 false, param_without_channel)));
494 chn_items.push_back (MenuElem (_("Show all channels"),
495 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
496 true, param_without_channel)));
498 for (uint8_t chn = 0; chn < 16; chn++) {
499 if (selected_channels & (0x0001 << chn)) {
501 /* for each selected channel, add a menu item for this controller */
503 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
504 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
505 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
506 fully_qualified_param)));
508 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
509 bool visible = false;
512 if (track->marked_for_display()) {
517 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
518 _channel_command_menu_map[fully_qualified_param] = cmi;
519 cmi->set_active (visible);
523 /* now create an item in the parent menu that has the per-channel list as a submenu */
525 items.push_back (MenuElem (label, *chn_menu));
529 /* just one channel - create a single menu item for this command+channel combination*/
531 for (uint8_t chn = 0; chn < 16; chn++) {
532 if (selected_channels & (0x0001 << chn)) {
534 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
535 items.push_back (CheckMenuElem (label,
536 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
537 fully_qualified_param)));
539 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
540 bool visible = false;
543 if (track->marked_for_display()) {
548 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
549 _channel_command_menu_map[fully_qualified_param] = cmi;
550 cmi->set_active (visible);
552 /* one channel only */
560 MidiTimeAxisView::build_controller_menu ()
562 using namespace Menu_Helpers;
564 if (controller_menu) {
565 /* it exists and has not been invalidated by a channel mode change, so just return it */
569 controller_menu = new Menu; // explicitly managed by us
570 MenuList& items (controller_menu->items());
572 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
573 for each controller+channel combination covering the currently selected channels for this track
576 uint16_t selected_channels = _channel_selector.get_selected_channels();
578 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
583 for (uint8_t chn = 0; chn < 16; chn++) {
584 if (selected_channels & (0x0001 << chn)) {
591 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
592 bank select controllers, as they are handled by the `patch' code */
594 for (int i = 0; i < 127; i += 16) {
596 Menu* ctl_menu = manage (new Menu);
597 MenuList& ctl_items (ctl_menu->items());
600 /* for each controller, consider whether to create a submenu or a single item */
602 for (int ctl = i; ctl < i+16; ++ctl) {
604 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
610 /* multiple channels - create a submenu, with 1 item per channel */
612 Menu* chn_menu = manage (new Menu);
613 MenuList& chn_items (chn_menu->items());
615 /* add a couple of items to hide/show this controller on all channels */
617 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
618 chn_items.push_back (MenuElem (_("Hide all channels"),
619 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
620 false, param_without_channel)));
621 chn_items.push_back (MenuElem (_("Show all channels"),
622 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
623 true, param_without_channel)));
625 for (uint8_t chn = 0; chn < 16; chn++) {
626 if (selected_channels & (0x0001 << chn)) {
628 /* for each selected channel, add a menu item for this controller */
630 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
631 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
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*>(&chn_items.back());
645 _controller_menu_map[fully_qualified_param] = cmi;
646 cmi->set_active (visible);
650 /* add the per-channel menu to the list of controllers, with the name of the controller */
651 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
652 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
656 /* just one channel - create a single menu item for this ctl+channel combination*/
658 for (uint8_t chn = 0; chn < 16; chn++) {
659 if (selected_channels & (0x0001 << chn)) {
661 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
662 ctl_items.push_back (
664 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
665 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
666 fully_qualified_param)
669 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
671 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
672 bool visible = false;
675 if (track->marked_for_display()) {
680 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
681 _controller_menu_map[fully_qualified_param] = cmi;
682 cmi->set_active (visible);
684 /* one channel only */
691 /* add the menu for this block of controllers to the overall controller menu */
693 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
698 MidiTimeAxisView::build_note_mode_menu()
700 using namespace Menu_Helpers;
702 Menu* mode_menu = manage (new Menu);
703 MenuList& items = mode_menu->items();
704 mode_menu->set_name ("ArdourContextMenu");
706 RadioMenuItem::Group mode_group;
707 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
708 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
709 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
710 _note_mode_item->set_active(_note_mode == Sustained);
712 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
713 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
714 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
715 _percussion_mode_item->set_active(_note_mode == Percussive);
721 MidiTimeAxisView::build_color_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, _("Meter Colors"),
731 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
732 MeterColors, false, true)));
733 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
734 _meter_color_mode_item->set_active(_color_mode == MeterColors);
736 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
737 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
738 ChannelColors, false, true)));
739 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
740 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
742 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
743 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
744 TrackColor, false, true)));
745 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
746 _channel_color_mode_item->set_active(_color_mode == TrackColor);
752 MidiTimeAxisView::set_note_mode(NoteMode mode)
754 if (_note_mode != mode || midi_track()->note_mode() != mode) {
756 midi_track()->set_note_mode(mode);
757 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
758 _view->redisplay_track();
763 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
765 if (_color_mode == mode && !force) {
769 if (mode == ChannelColors) {
770 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
772 _channel_selector.set_default_channel_color();
776 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
778 _view->redisplay_track();
783 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
785 if (!_ignore_signals)
786 midi_view()->set_note_range(range);
791 MidiTimeAxisView::update_range()
793 MidiGhostRegion* mgr;
795 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
796 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
803 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
805 if (apply_to_selection) {
806 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
809 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
811 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
812 create_automation_child(*i, true);
816 RouteTimeAxisView::show_all_automation ();
821 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
823 if (apply_to_selection) {
824 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
827 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
829 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
830 create_automation_child(*i, true);
834 RouteTimeAxisView::show_existing_automation ();
838 /** Create an automation track for the given parameter (pitch bend, channel pressure).
841 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
843 if (param.type() == NullAutomation) {
844 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
848 AutomationTracks::iterator existing = _automation_tracks.find (param);
850 if (existing != _automation_tracks.end()) {
852 /* automation track created because we had existing data for
853 * the processor, but visibility may need to be controlled
854 * since it will have been set visible by default.
857 existing->second->set_visibility (show);
860 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
866 boost::shared_ptr<AutomationTimeAxisView> track;
868 switch (param.type()) {
871 create_gain_automation_child (param, show);
874 case PluginAutomation:
875 /* handled elsewhere */
878 case MidiCCAutomation:
879 case MidiPgmChangeAutomation:
880 case MidiPitchBenderAutomation:
881 case MidiChannelPressureAutomation:
882 case MidiSystemExclusiveAutomation:
883 /* These controllers are region "automation" - they are owned
884 * by regions (and their MidiModels), not by the track. As a
885 * result we do not create an AutomationList/Line for the track
886 * ... except here we are doing something!! XXX
889 track.reset (new AutomationTimeAxisView (
892 boost::shared_ptr<Automatable> (),
893 boost::shared_ptr<AutomationControl> (),
899 _route->describe_parameter(param)
903 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
906 add_automation_child (param, track, show);
910 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
915 MidiTimeAxisView::route_active_changed ()
917 RouteUI::route_active_changed ();
920 if (_route->active()) {
921 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
922 controls_base_selected_name = "MidiTrackControlsBaseSelected";
923 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
925 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
926 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
927 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
933 if (_route->active()) {
934 controls_ebox.set_name ("BusControlsBaseUnselected");
935 controls_base_selected_name = "BusControlsBaseSelected";
936 controls_base_unselected_name = "BusControlsBaseUnselected";
938 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
939 controls_base_selected_name = "BusControlsBaseInactiveSelected";
940 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
948 MidiTimeAxisView::add_note_selection (uint8_t note)
950 if (!_editor.internal_editing()) {
954 uint16_t chn_mask = _channel_selector.get_selected_channels();
956 if (_view->num_selected_regionviews() == 0) {
957 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
959 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
964 MidiTimeAxisView::extend_note_selection (uint8_t note)
966 if (!_editor.internal_editing()) {
970 uint16_t chn_mask = _channel_selector.get_selected_channels();
972 if (_view->num_selected_regionviews() == 0) {
973 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
975 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
980 MidiTimeAxisView::toggle_note_selection (uint8_t note)
982 if (!_editor.internal_editing()) {
986 uint16_t chn_mask = _channel_selector.get_selected_channels();
988 if (_view->num_selected_regionviews() == 0) {
989 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
991 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
996 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
998 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1002 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1004 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1008 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1010 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1014 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1016 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1020 uint16_t selected_channels = _channel_selector.get_selected_channels();
1021 bool changed = false;
1025 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1027 for (uint32_t chn = 0; chn < 16; ++chn) {
1028 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1029 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1035 if ((selected_channels & (0x0001 << chn)) == 0) {
1036 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1037 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1039 changed = track->set_visibility (false) || changed;
1041 changed = track->set_visibility (true) || changed;
1048 /* TODO: Bender, Pressure */
1050 /* invalidate the controller menu, so that we rebuild it next time */
1051 _controller_menu_map.clear ();
1052 delete controller_menu;
1053 controller_menu = 0;
1056 _route->gui_changed ("track_height", this);
1061 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1063 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1068 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1069 if (i != _controller_menu_map.end()) {
1073 i = _channel_command_menu_map.find (param);
1074 if (i != _channel_command_menu_map.end()) {
1081 boost::shared_ptr<MidiRegion>
1082 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1084 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1086 real_editor->begin_reversible_command (Operations::create_region);
1087 playlist()->clear_changes ();
1089 real_editor->snap_to (pos, 0);
1091 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1092 view()->trackview().track()->name());
1095 plist.add (ARDOUR::Properties::start, 0);
1096 plist.add (ARDOUR::Properties::length, length);
1097 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1099 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1101 playlist()->add_region (region, pos);
1102 _session->add_command (new StatefulDiffCommand (playlist()));
1105 real_editor->commit_reversible_command ();
1108 return boost::dynamic_pointer_cast<MidiRegion>(region);
1112 MidiTimeAxisView::ensure_step_editor ()
1114 if (!_step_editor) {
1115 _step_editor = new StepEditor (_editor, midi_track(), *this);
1120 MidiTimeAxisView::start_step_editing ()
1122 ensure_step_editor ();
1123 _step_editor->start_step_editing ();
1127 MidiTimeAxisView::stop_step_editing ()
1130 _step_editor->stop_step_editing ();
1135 /** @return channel (counted from 0) to add an event to, based on the current setting
1136 * of the channel selector.
1139 MidiTimeAxisView::get_channel_for_add () const
1141 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1143 uint8_t channel = 0;
1145 /* pick the highest selected channel, unless all channels are selected,
1146 which is interpreted to mean channel 1 (zero)
1149 for (uint16_t i = 0; i < 16; ++i) {
1150 if (chn_mask & (1<<i)) {
1156 if (chn_cnt == 16) {