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, Canvas& canvas)
103 : AxisView(sess) // virtually inherited
104 , RouteTimeAxisView(ed, sess, canvas)
105 , _ignore_signals(false)
107 , _piano_roll_header(0)
108 , _note_mode(Sustained)
110 , _percussion_mode_item(0)
111 , _color_mode(MeterColors)
112 , _meter_color_mode_item(0)
113 , _channel_color_mode_item(0)
114 , _track_color_mode_item(0)
115 , _step_edit_item (0)
116 , _midi_thru_item (0)
117 , controller_menu (0)
123 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
125 RouteTimeAxisView::set_route (rt);
127 subplugin_menu.set_name ("ArdourContextMenu");
129 _view = new MidiStreamView (*this);
131 ignore_toggle = false;
133 mute_button->set_active (false);
134 solo_button->set_active (false);
136 if (is_midi_track()) {
137 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
138 _note_mode = midi_track()->note_mode();
139 } else { // MIDI bus (which doesn't exist yet..)
140 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
143 /* map current state of the route */
145 processors_changed (RouteProcessorChange ());
147 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
150 _piano_roll_header = new PianoRollHeader(*midi_view());
152 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
153 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
154 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
156 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
158 /* Suspend updates of the StreamView during scroomer drags to speed things up */
159 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
160 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
162 controls_hbox.pack_start(*_range_scroomer);
163 controls_hbox.pack_start(*_piano_roll_header);
165 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
166 controls_base_selected_name = "MidiTrackControlsBaseSelected";
167 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
169 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
171 /* ask for notifications of any new RegionViews */
172 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
174 if (!_editor.have_idled()) {
175 /* first idle will do what we need */
181 HBox* midi_controls_hbox = manage(new HBox());
183 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
185 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
186 for (; m != patch_manager.all_models().end(); ++m) {
187 _model_selector.append_text(m->c_str());
190 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
192 _custom_device_mode_selector.signal_changed().connect(
193 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
195 // TODO: persist the choice
196 // this initializes the comboboxes and sends out the signal
197 _model_selector.set_active(0);
199 midi_controls_hbox->pack_start(_channel_selector, true, false);
200 if (!patch_manager.all_models().empty()) {
201 _midi_controls_box.pack_start(_model_selector, true, false);
202 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
205 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
207 controls_vbox.pack_start(_midi_controls_box, false, false);
209 // restore channel selector settings
210 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
211 _channel_selector.mode_changed.connect(
212 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
213 _channel_selector.mode_changed.connect(
214 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
216 string prop = gui_property ("color-mode");
218 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
219 if (_color_mode == ChannelColors) {
220 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
224 set_color_mode (_color_mode, true, false);
226 prop = gui_property ("note-mode");
228 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
229 if (_percussion_mode_item) {
230 _percussion_mode_item->set_active (_note_mode == Percussive);
236 MidiTimeAxisView::first_idle ()
243 MidiTimeAxisView::~MidiTimeAxisView ()
245 delete _piano_roll_header;
246 _piano_roll_header = 0;
248 delete _range_scroomer;
251 delete controller_menu;
256 MidiTimeAxisView::enter_internal_edit_mode ()
259 midi_view()->enter_internal_edit_mode ();
264 MidiTimeAxisView::leave_internal_edit_mode ()
267 midi_view()->leave_internal_edit_mode ();
272 MidiTimeAxisView::check_step_edit ()
274 ensure_step_editor ();
275 _step_editor->check_step_edit ();
279 MidiTimeAxisView::model_changed()
281 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
282 .custom_device_mode_names_by_model(_model_selector.get_active_text());
284 _custom_device_mode_selector.clear_items();
286 for (std::list<std::string>::const_iterator i = device_modes.begin();
287 i != device_modes.end(); ++i) {
288 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
289 _custom_device_mode_selector.append_text(*i);
292 _custom_device_mode_selector.set_active(0);
295 void MidiTimeAxisView::custom_device_mode_changed()
297 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
298 _custom_device_mode_selector.get_active_text());
302 MidiTimeAxisView::midi_view()
304 return dynamic_cast<MidiStreamView*>(_view);
308 MidiTimeAxisView::set_height (uint32_t h)
310 RouteTimeAxisView::set_height (h);
312 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
313 _midi_controls_box.show_all ();
315 _midi_controls_box.hide();
318 if (height >= KEYBOARD_MIN_HEIGHT) {
319 if (is_track() && _range_scroomer)
320 _range_scroomer->show();
321 if (is_track() && _piano_roll_header)
322 _piano_roll_header->show();
324 if (is_track() && _range_scroomer)
325 _range_scroomer->hide();
326 if (is_track() && _piano_roll_header)
327 _piano_roll_header->hide();
332 MidiTimeAxisView::append_extra_display_menu_items ()
334 using namespace Menu_Helpers;
336 MenuList& items = display_menu->items();
339 Menu *range_menu = manage(new Menu);
340 MenuList& range_items = range_menu->items();
341 range_menu->set_name ("ArdourContextMenu");
343 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
344 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
345 MidiStreamView::FullRange)));
347 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
348 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
349 MidiStreamView::ContentsRange)));
351 items.push_back (MenuElem (_("Note Range"), *range_menu));
352 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
354 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
355 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
357 items.push_back (SeparatorElem ());
361 MidiTimeAxisView::toggle_midi_thru ()
363 if (!_midi_thru_item) {
367 bool view_yn = _midi_thru_item->get_active();
368 if (view_yn != midi_track()->midi_thru()) {
369 midi_track()->set_midi_thru (view_yn);
374 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
376 using namespace Menu_Helpers;
378 /* If we have a controller menu, we need to detach it before
379 RouteTimeAxis::build_automation_action_menu destroys the
380 menu it is attached to. Otherwise GTK destroys
381 controller_menu's gobj, meaning that it can't be reattached
382 below. See bug #3134.
385 if (controller_menu) {
386 detach_menu (*controller_menu);
389 _channel_command_menu_map.clear ();
390 RouteTimeAxisView::build_automation_action_menu (for_selection);
392 MenuList& automation_items = automation_action_menu->items();
394 uint16_t selected_channels = _channel_selector.get_selected_channels();
396 if (selected_channels != 0) {
398 automation_items.push_back (SeparatorElem());
400 /* these 2 MIDI "command" types are semantically more like automation than note data,
401 but they are not MIDI controllers. We give them special status in this menu, since
402 they will not show up in the controller list and anyone who actually knows
403 something about MIDI (!) would not expect to find them there.
406 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
407 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
408 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
409 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
411 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
412 since it might need to be updated after a channel mode change or other change. Also detach it
413 first in case it has been used anywhere else.
416 build_controller_menu ();
418 automation_items.push_back (SeparatorElem());
419 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
420 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
422 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
423 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
429 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
431 uint16_t selected_channels = _channel_selector.get_selected_channels();
433 for (uint8_t chn = 0; chn < 16; chn++) {
434 if (selected_channels & (0x0001 << chn)) {
436 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
437 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
440 menu->set_active (yn);
447 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
449 using namespace Menu_Helpers;
451 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
454 uint16_t selected_channels = _channel_selector.get_selected_channels();
457 for (uint8_t chn = 0; chn < 16; chn++) {
458 if (selected_channels & (0x0001 << chn)) {
467 /* multiple channels - create a submenu, with 1 item per channel */
469 Menu* chn_menu = manage (new Menu);
470 MenuList& chn_items (chn_menu->items());
471 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
473 /* add a couple of items to hide/show all of them */
475 chn_items.push_back (MenuElem (_("Hide all channels"),
476 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
477 false, param_without_channel)));
478 chn_items.push_back (MenuElem (_("Show all channels"),
479 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
480 true, param_without_channel)));
482 for (uint8_t chn = 0; chn < 16; chn++) {
483 if (selected_channels & (0x0001 << chn)) {
485 /* for each selected channel, add a menu item for this controller */
487 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
488 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
489 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
490 fully_qualified_param)));
492 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
493 bool visible = false;
496 if (track->marked_for_display()) {
501 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
502 _channel_command_menu_map[fully_qualified_param] = cmi;
503 cmi->set_active (visible);
507 /* now create an item in the parent menu that has the per-channel list as a submenu */
509 items.push_back (MenuElem (label, *chn_menu));
513 /* just one channel - create a single menu item for this command+channel combination*/
515 for (uint8_t chn = 0; chn < 16; chn++) {
516 if (selected_channels & (0x0001 << chn)) {
518 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
519 items.push_back (CheckMenuElem (label,
520 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
521 fully_qualified_param)));
523 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
524 bool visible = false;
527 if (track->marked_for_display()) {
532 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
533 _channel_command_menu_map[fully_qualified_param] = cmi;
534 cmi->set_active (visible);
536 /* one channel only */
544 MidiTimeAxisView::build_controller_menu ()
546 using namespace Menu_Helpers;
548 if (controller_menu) {
549 /* it exists and has not been invalidated by a channel mode change, so just return it */
553 controller_menu = new Menu; // explicitly managed by us
554 MenuList& items (controller_menu->items());
556 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
557 for each controller+channel combination covering the currently selected channels for this track
560 uint16_t selected_channels = _channel_selector.get_selected_channels();
562 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
567 for (uint8_t chn = 0; chn < 16; chn++) {
568 if (selected_channels & (0x0001 << chn)) {
575 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
576 bank select controllers, as they are handled by the `patch' code */
578 for (int i = 0; i < 127; i += 16) {
580 Menu* ctl_menu = manage (new Menu);
581 MenuList& ctl_items (ctl_menu->items());
584 /* for each controller, consider whether to create a submenu or a single item */
586 for (int ctl = i; ctl < i+16; ++ctl) {
588 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
594 /* multiple channels - create a submenu, with 1 item per channel */
596 Menu* chn_menu = manage (new Menu);
597 MenuList& chn_items (chn_menu->items());
599 /* add a couple of items to hide/show this controller on all channels */
601 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
602 chn_items.push_back (MenuElem (_("Hide all channels"),
603 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
604 false, param_without_channel)));
605 chn_items.push_back (MenuElem (_("Show all channels"),
606 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
607 true, param_without_channel)));
609 for (uint8_t chn = 0; chn < 16; chn++) {
610 if (selected_channels & (0x0001 << chn)) {
612 /* for each selected channel, add a menu item for this controller */
614 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
615 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
616 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
617 fully_qualified_param)));
619 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
620 bool visible = false;
623 if (track->marked_for_display()) {
628 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
629 _controller_menu_map[fully_qualified_param] = cmi;
630 cmi->set_active (visible);
634 /* add the per-channel menu to the list of controllers, with the name of the controller */
635 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
636 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
640 /* just one channel - create a single menu item for this ctl+channel combination*/
642 for (uint8_t chn = 0; chn < 16; chn++) {
643 if (selected_channels & (0x0001 << chn)) {
645 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
646 ctl_items.push_back (
648 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
649 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
650 fully_qualified_param)
653 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
655 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
656 bool visible = false;
659 if (track->marked_for_display()) {
664 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
665 _controller_menu_map[fully_qualified_param] = cmi;
666 cmi->set_active (visible);
668 /* one channel only */
675 /* add the menu for this block of controllers to the overall controller menu */
677 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
682 MidiTimeAxisView::build_note_mode_menu()
684 using namespace Menu_Helpers;
686 Menu* mode_menu = manage (new Menu);
687 MenuList& items = mode_menu->items();
688 mode_menu->set_name ("ArdourContextMenu");
690 RadioMenuItem::Group mode_group;
691 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
692 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
693 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
694 _note_mode_item->set_active(_note_mode == Sustained);
696 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
697 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
698 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
699 _percussion_mode_item->set_active(_note_mode == Percussive);
705 MidiTimeAxisView::build_color_mode_menu()
707 using namespace Menu_Helpers;
709 Menu* mode_menu = manage (new Menu);
710 MenuList& items = mode_menu->items();
711 mode_menu->set_name ("ArdourContextMenu");
713 RadioMenuItem::Group mode_group;
714 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
715 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
716 MeterColors, false, true)));
717 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
718 _meter_color_mode_item->set_active(_color_mode == MeterColors);
720 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
721 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
722 ChannelColors, false, true)));
723 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
724 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
726 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
727 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
728 TrackColor, false, true)));
729 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
730 _channel_color_mode_item->set_active(_color_mode == TrackColor);
736 MidiTimeAxisView::set_note_mode(NoteMode mode)
738 if (_note_mode != mode || midi_track()->note_mode() != mode) {
740 midi_track()->set_note_mode(mode);
741 set_gui_property ("note-mode", enum_2_string(_note_mode));
742 _view->redisplay_track();
747 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
749 if (_color_mode == mode && !force) {
753 if (mode == ChannelColors) {
754 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
756 _channel_selector.set_default_channel_color();
760 set_gui_property ("color-mode", enum_2_string(_color_mode));
762 _view->redisplay_track();
767 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
769 if (!_ignore_signals)
770 midi_view()->set_note_range(range);
775 MidiTimeAxisView::update_range()
777 MidiGhostRegion* mgr;
779 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
780 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
787 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
789 if (apply_to_selection) {
790 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
793 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
795 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
796 create_automation_child(*i, true);
800 RouteTimeAxisView::show_all_automation ();
805 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
807 if (apply_to_selection) {
808 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
811 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
813 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
814 create_automation_child (*i, true);
818 RouteTimeAxisView::show_existing_automation ();
822 /** Create an automation track for the given parameter (pitch bend, channel pressure).
825 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
827 if (param.type() == NullAutomation) {
828 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
832 AutomationTracks::iterator existing = _automation_tracks.find (param);
834 if (existing != _automation_tracks.end()) {
836 /* automation track created because we had existing data for
837 * the processor, but visibility may need to be controlled
838 * since it will have been set visible by default.
841 cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
843 if (existing->second->set_marked_for_display (show) && !no_redraw) {
850 boost::shared_ptr<AutomationTimeAxisView> track;
852 switch (param.type()) {
855 create_gain_automation_child (param, show);
858 case PluginAutomation:
859 /* handled elsewhere */
862 case MidiCCAutomation:
863 case MidiPgmChangeAutomation:
864 case MidiPitchBenderAutomation:
865 case MidiChannelPressureAutomation:
866 case MidiSystemExclusiveAutomation:
867 /* These controllers are region "automation" - they are owned
868 * by regions (and their MidiModels), not by the track. As a
869 * result we do not create an AutomationList/Line for the track
870 * ... except here we are doing something!! XXX
873 track.reset (new AutomationTimeAxisView (
876 boost::shared_ptr<Automatable> (),
877 boost::shared_ptr<AutomationControl> (),
883 _route->describe_parameter(param)
887 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
890 add_automation_child (param, track, show);
894 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
899 MidiTimeAxisView::route_active_changed ()
901 RouteUI::route_active_changed ();
904 if (_route->active()) {
905 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
906 controls_base_selected_name = "MidiTrackControlsBaseSelected";
907 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
909 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
910 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
911 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
917 if (_route->active()) {
918 controls_ebox.set_name ("BusControlsBaseUnselected");
919 controls_base_selected_name = "BusControlsBaseSelected";
920 controls_base_unselected_name = "BusControlsBaseUnselected";
922 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
923 controls_base_selected_name = "BusControlsBaseInactiveSelected";
924 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
932 MidiTimeAxisView::add_note_selection (uint8_t note)
934 if (!_editor.internal_editing()) {
938 uint16_t chn_mask = _channel_selector.get_selected_channels();
940 if (_view->num_selected_regionviews() == 0) {
941 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
943 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
948 MidiTimeAxisView::extend_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::extend_note_selection_region_view), note, chn_mask));
959 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
964 MidiTimeAxisView::toggle_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::toggle_note_selection_region_view), note, chn_mask));
975 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
980 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
982 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
986 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
988 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
992 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
994 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
998 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1000 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1004 uint16_t selected_channels = _channel_selector.get_selected_channels();
1005 bool changed = false;
1009 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1011 for (uint32_t chn = 0; chn < 16; ++chn) {
1012 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1013 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1019 if ((selected_channels & (0x0001 << chn)) == 0) {
1020 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1021 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1023 changed = track->set_marked_for_display (false) || changed;
1025 changed = track->set_marked_for_display (true) || changed;
1032 /* TODO: Bender, Pressure */
1034 /* invalidate the controller menu, so that we rebuild it next time */
1035 _controller_menu_map.clear ();
1036 delete controller_menu;
1037 controller_menu = 0;
1045 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1047 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1052 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1053 if (i != _controller_menu_map.end()) {
1057 i = _channel_command_menu_map.find (param);
1058 if (i != _channel_command_menu_map.end()) {
1065 boost::shared_ptr<MidiRegion>
1066 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1068 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1070 real_editor->begin_reversible_command (Operations::create_region);
1071 playlist()->clear_changes ();
1073 real_editor->snap_to (pos, 0);
1075 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1076 view()->trackview().track()->name());
1079 plist.add (ARDOUR::Properties::start, 0);
1080 plist.add (ARDOUR::Properties::length, length);
1081 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1083 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1085 playlist()->add_region (region, pos);
1086 _session->add_command (new StatefulDiffCommand (playlist()));
1089 real_editor->commit_reversible_command ();
1092 return boost::dynamic_pointer_cast<MidiRegion>(region);
1096 MidiTimeAxisView::ensure_step_editor ()
1098 if (!_step_editor) {
1099 _step_editor = new StepEditor (_editor, midi_track(), *this);
1104 MidiTimeAxisView::start_step_editing ()
1106 ensure_step_editor ();
1107 _step_editor->start_step_editing ();
1111 MidiTimeAxisView::stop_step_editing ()
1114 _step_editor->stop_step_editing ();
1119 /** @return channel (counted from 0) to add an event to, based on the current setting
1120 * of the channel selector.
1123 MidiTimeAxisView::get_channel_for_add () const
1125 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1127 uint8_t channel = 0;
1129 /* pick the highest selected channel, unless all channels are selected,
1130 which is interpreted to mean channel 1 (zero)
1133 for (uint16_t i = 0; i < 16; ++i) {
1134 if (chn_mask & (1<<i)) {
1140 if (chn_cnt == 16) {