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"
56 #include "midi++/names.h"
58 #include "add_midi_cc_track_dialog.h"
59 #include "ardour_ui.h"
60 #include "automation_line.h"
61 #include "automation_time_axis.h"
62 #include "canvas-note-event.h"
63 #include "canvas_impl.h"
64 #include "crossfade_view.h"
67 #include "ghostregion.h"
68 #include "gui_thread.h"
70 #include "midi_scroomer.h"
71 #include "midi_streamview.h"
72 #include "midi_region_view.h"
73 #include "midi_time_axis.h"
74 #include "piano_roll_header.h"
75 #include "playlist_selector.h"
76 #include "plugin_selector.h"
77 #include "plugin_ui.h"
78 #include "point_selection.h"
80 #include "region_view.h"
81 #include "rgb_macros.h"
82 #include "selection.h"
83 #include "simplerect.h"
86 #include "ardour/midi_track.h"
90 using namespace ARDOUR;
93 using namespace Gtkmm2ext;
94 using namespace Editing;
96 // Minimum height at which a control is displayed
97 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
98 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
100 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
101 boost::shared_ptr<Route> rt, Canvas& canvas)
102 : AxisView(sess) // virtually inherited
103 , RouteTimeAxisView(ed, sess, rt, canvas)
104 , _ignore_signals(false)
106 , _piano_roll_header(0)
107 , _note_mode(Sustained)
109 , _percussion_mode_item(0)
110 , _color_mode(MeterColors)
111 , _meter_color_mode_item(0)
112 , _channel_color_mode_item(0)
113 , _track_color_mode_item(0)
114 , _step_edit_item (0)
115 , _midi_thru_item (0)
116 , default_channel_menu (0)
117 , controller_menu (0)
119 subplugin_menu.set_name ("ArdourContextMenu");
121 _view = new MidiStreamView (*this);
123 ignore_toggle = false;
125 mute_button->set_active (false);
126 solo_button->set_active (false);
128 step_edit_insert_position = 0;
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 controls_hbox.pack_start(*_range_scroomer);
157 controls_hbox.pack_start(*_piano_roll_header);
159 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
160 controls_base_selected_name = "MidiTrackControlsBaseSelected";
161 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
163 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
165 /* ask for notifications of any new RegionViews */
166 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
169 midi_track()->PlaylistChanged.connect (*this, invalidator (*this),
170 boost::bind (&MidiTimeAxisView::playlist_changed, this),
176 HBox* midi_controls_hbox = manage(new HBox());
178 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
180 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
181 for (; m != patch_manager.all_models().end(); ++m) {
182 _model_selector.append_text(m->c_str());
185 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
187 _custom_device_mode_selector.signal_changed().connect(
188 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
190 // TODO: persist the choice
191 // this initializes the comboboxes and sends out the signal
192 _model_selector.set_active(0);
194 midi_controls_hbox->pack_start(_channel_selector, true, false);
195 if (!patch_manager.all_models().empty()) {
196 _midi_controls_box.pack_start(_model_selector, true, false);
197 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
200 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
202 controls_vbox.pack_start(_midi_controls_box, false, false);
204 // restore channel selector settings
205 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
206 _channel_selector.mode_changed.connect(
207 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
208 _channel_selector.mode_changed.connect(
209 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
212 if ((prop = xml_node->property ("color-mode")) != 0) {
213 _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
214 if (_color_mode == ChannelColors) {
215 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
219 if ((prop = xml_node->property ("note-mode")) != 0) {
220 _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
221 if (_percussion_mode_item) {
222 _percussion_mode_item->set_active (_note_mode == Percussive);
227 MidiTimeAxisView::~MidiTimeAxisView ()
229 delete _piano_roll_header;
230 _piano_roll_header = 0;
232 delete _range_scroomer;
235 delete controller_menu;
239 MidiTimeAxisView::playlist_changed ()
241 step_edit_region_connection.disconnect ();
242 midi_track()->playlist()->RegionRemoved.connect (step_edit_region_connection, invalidator (*this),
243 ui_bind (&MidiTimeAxisView::region_removed, this, _1),
248 MidiTimeAxisView::region_removed (boost::weak_ptr<Region> wr)
250 boost::shared_ptr<Region> r (wr.lock());
256 if (step_edit_region == r) {
257 step_edit_region.reset();
258 // force a recompute of the insert position
259 step_edit_beat_pos = -1.0;
263 void MidiTimeAxisView::model_changed()
265 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
266 .custom_device_mode_names_by_model(_model_selector.get_active_text());
268 _custom_device_mode_selector.clear_items();
270 for (std::list<std::string>::const_iterator i = device_modes.begin();
271 i != device_modes.end(); ++i) {
272 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
273 _custom_device_mode_selector.append_text(*i);
276 _custom_device_mode_selector.set_active(0);
279 void MidiTimeAxisView::custom_device_mode_changed()
281 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
282 _custom_device_mode_selector.get_active_text());
286 MidiTimeAxisView::midi_view()
288 return dynamic_cast<MidiStreamView*>(_view);
292 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
295 xml_node->add_property ("shown-editor", "yes");
297 guint32 ret = TimeAxisView::show_at (y, nth, parent);
302 MidiTimeAxisView::hide ()
305 xml_node->add_property ("shown-editor", "no");
307 TimeAxisView::hide ();
311 MidiTimeAxisView::set_height (uint32_t h)
313 RouteTimeAxisView::set_height (h);
315 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
316 _midi_controls_box.show_all ();
318 _midi_controls_box.hide();
321 if (height >= KEYBOARD_MIN_HEIGHT) {
322 if (is_track() && _range_scroomer)
323 _range_scroomer->show();
324 if (is_track() && _piano_roll_header)
325 _piano_roll_header->show();
327 if (is_track() && _range_scroomer)
328 _range_scroomer->hide();
329 if (is_track() && _piano_roll_header)
330 _piano_roll_header->hide();
335 MidiTimeAxisView::append_extra_display_menu_items ()
337 using namespace Menu_Helpers;
339 MenuList& items = display_menu->items();
342 Menu *range_menu = manage(new Menu);
343 MenuList& range_items = range_menu->items();
344 range_menu->set_name ("ArdourContextMenu");
346 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
347 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
348 MidiStreamView::FullRange)));
350 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
351 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
352 MidiStreamView::ContentsRange)));
354 items.push_back (MenuElem (_("Note range"), *range_menu));
355 items.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
356 items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
358 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
359 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
363 MidiTimeAxisView::build_def_channel_menu ()
365 using namespace Menu_Helpers;
367 default_channel_menu = manage (new Menu ());
369 uint8_t defchn = midi_track()->default_channel();
370 MenuList& def_channel_items = default_channel_menu->items();
372 RadioMenuItem::Group dc_group;
374 for (int i = 0; i < 16; ++i) {
376 snprintf (buf, sizeof (buf), "%d", i+1);
378 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
379 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
380 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
381 item->set_active ((i == defchn));
384 return default_channel_menu;
388 MidiTimeAxisView::set_default_channel (int chn)
390 midi_track()->set_default_channel (chn);
394 MidiTimeAxisView::toggle_midi_thru ()
396 if (!_midi_thru_item) {
400 bool view_yn = _midi_thru_item->get_active();
401 if (view_yn != midi_track()->midi_thru()) {
402 midi_track()->set_midi_thru (view_yn);
407 MidiTimeAxisView::build_automation_action_menu ()
409 using namespace Menu_Helpers;
411 /* If we have a controller menu, we need to detach it before
412 RouteTimeAxis::build_automation_action_menu destroys the
413 menu it is attached to. Otherwise GTK destroys
414 controller_menu's gobj, meaning that it can't be reattached
415 below. See bug #3134.
418 if (controller_menu) {
419 detach_menu (*controller_menu);
422 _channel_command_menu_map.clear ();
423 RouteTimeAxisView::build_automation_action_menu ();
425 MenuList& automation_items = automation_action_menu->items();
427 uint16_t selected_channels = _channel_selector.get_selected_channels();
429 if (selected_channels != 0) {
431 automation_items.push_back (SeparatorElem());
433 /* these 3 MIDI "command" types are semantically more like automation than note data,
434 but they are not MIDI controllers. We give them special status in this menu, since
435 they will not show up in the controller list and anyone who actually knows
436 something about MIDI (!) would not expect to find them there.
439 add_channel_command_menu_item (automation_items, _("Program Change"), MidiPgmChangeAutomation, 0);
440 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
441 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
443 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
444 since it might need to be updated after a channel mode change or other change. Also detach it
445 first in case it has been used anywhere else.
448 build_controller_menu ();
450 automation_items.push_back (SeparatorElem());
451 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
453 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
459 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
461 uint16_t selected_channels = _channel_selector.get_selected_channels();
463 for (uint8_t chn = 0; chn < 16; chn++) {
464 if (selected_channels & (0x0001 << chn)) {
466 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
467 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
470 menu->set_active (yn);
477 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
479 using namespace Menu_Helpers;
481 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
484 uint16_t selected_channels = _channel_selector.get_selected_channels();
487 for (uint8_t chn = 0; chn < 16; chn++) {
488 if (selected_channels & (0x0001 << chn)) {
497 /* multiple channels - create a submenu, with 1 item per channel */
499 Menu* chn_menu = manage (new Menu);
500 MenuList& chn_items (chn_menu->items());
501 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
503 /* add a couple of items to hide/show all of them */
505 chn_items.push_back (MenuElem (_("Hide all channels"),
506 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
507 false, param_without_channel)));
508 chn_items.push_back (MenuElem (_("Show all channels"),
509 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
510 true, param_without_channel)));
512 for (uint8_t chn = 0; chn < 16; chn++) {
513 if (selected_channels & (0x0001 << chn)) {
515 /* for each selected channel, add a menu item for this controller */
517 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
518 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
519 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
520 fully_qualified_param)));
522 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
523 bool visible = false;
526 if (track->marked_for_display()) {
531 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
532 _channel_command_menu_map[fully_qualified_param] = cmi;
533 cmi->set_active (visible);
537 /* now create an item in the parent menu that has the per-channel list as a submenu */
539 items.push_back (MenuElem (label, *chn_menu));
543 /* just one channel - create a single menu item for this command+channel combination*/
545 for (uint8_t chn = 0; chn < 16; chn++) {
546 if (selected_channels & (0x0001 << chn)) {
548 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
549 items.push_back (CheckMenuElem (label,
550 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
551 fully_qualified_param)));
553 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
554 bool visible = false;
557 if (track->marked_for_display()) {
562 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
563 _channel_command_menu_map[fully_qualified_param] = cmi;
564 cmi->set_active (visible);
566 /* one channel only */
574 MidiTimeAxisView::build_controller_menu ()
576 using namespace Menu_Helpers;
578 if (controller_menu) {
579 /* it exists and has not been invalidated by a channel mode change, so just return it */
583 controller_menu = new Menu; // explicitly managed by us
584 MenuList& items (controller_menu->items());
586 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
587 for each controller+channel combination covering the currently selected channels for this track
590 uint16_t selected_channels = _channel_selector.get_selected_channels();
592 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
597 for (uint8_t chn = 0; chn < 16; chn++) {
598 if (selected_channels & (0x0001 << chn)) {
605 /* loop over all 127 MIDI controllers, in groups of 16 */
607 for (int i = 0; i < 127; i += 16) {
609 Menu* ctl_menu = manage (new Menu);
610 MenuList& ctl_items (ctl_menu->items());
613 /* for each controller, consider whether to create a submenu or a single item */
615 for (int ctl = i; ctl < i+16; ++ctl) {
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 (CheckMenuElem (_route->describe_parameter (fully_qualified_param),
672 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
673 fully_qualified_param)));
675 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
676 bool visible = false;
679 if (track->marked_for_display()) {
684 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
685 _controller_menu_map[fully_qualified_param] = cmi;
686 cmi->set_active (visible);
688 /* one channel only */
695 /* add the menu for this block of controllers to the overall controller menu */
697 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
702 MidiTimeAxisView::build_note_mode_menu()
704 using namespace Menu_Helpers;
706 Menu* mode_menu = manage (new Menu);
707 MenuList& items = mode_menu->items();
708 mode_menu->set_name ("ArdourContextMenu");
710 RadioMenuItem::Group mode_group;
711 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
712 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
713 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
714 _note_mode_item->set_active(_note_mode == Sustained);
716 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
717 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
718 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
719 _percussion_mode_item->set_active(_note_mode == Percussive);
725 MidiTimeAxisView::build_color_mode_menu()
727 using namespace Menu_Helpers;
729 Menu* mode_menu = manage (new Menu);
730 MenuList& items = mode_menu->items();
731 mode_menu->set_name ("ArdourContextMenu");
733 RadioMenuItem::Group mode_group;
734 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
735 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), MeterColors)));
736 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
737 _meter_color_mode_item->set_active(_color_mode == MeterColors);
739 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
740 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
741 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
742 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
744 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
745 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
746 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
747 _channel_color_mode_item->set_active(_color_mode == TrackColor);
753 MidiTimeAxisView::set_note_mode(NoteMode mode)
755 if (_note_mode != mode || midi_track()->note_mode() != mode) {
757 midi_track()->set_note_mode(mode);
758 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
759 _view->redisplay_track();
764 MidiTimeAxisView::set_color_mode(ColorMode mode)
766 if (_color_mode != mode) {
767 if (mode == ChannelColors) {
768 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
770 _channel_selector.set_default_channel_color();
774 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
775 _view->redisplay_track();
780 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
782 if (!_ignore_signals)
783 midi_view()->set_note_range(range);
788 MidiTimeAxisView::update_range()
790 MidiGhostRegion* mgr;
792 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
793 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
800 MidiTimeAxisView::show_all_automation ()
803 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
805 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
806 create_automation_child(*i, true);
810 RouteTimeAxisView::show_all_automation ();
814 MidiTimeAxisView::show_existing_automation ()
817 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
819 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
820 create_automation_child(*i, true);
824 RouteTimeAxisView::show_existing_automation ();
827 /** Create an automation track for the given parameter (pitch bend, channel pressure).
830 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
832 /* These controllers are region "automation", so we do not create
833 * an AutomationList/Line for the track */
835 if (param.type() == NullAutomation) {
836 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
840 AutomationTracks::iterator existing = _automation_tracks.find (param);
841 if (existing != _automation_tracks.end()) {
845 boost::shared_ptr<AutomationControl> c = _route->get_control (param);
849 boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session,
850 _route, boost::shared_ptr<ARDOUR::Automatable>(), c,
855 _route->describe_parameter(param)));
857 add_automation_child (param, track, show);
862 MidiTimeAxisView::route_active_changed ()
864 RouteUI::route_active_changed ();
867 if (_route->active()) {
868 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
869 controls_base_selected_name = "MidiTrackControlsBaseSelected";
870 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
872 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
873 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
874 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
880 if (_route->active()) {
881 controls_ebox.set_name ("BusControlsBaseUnselected");
882 controls_base_selected_name = "BusControlsBaseSelected";
883 controls_base_unselected_name = "BusControlsBaseUnselected";
885 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
886 controls_base_selected_name = "BusControlsBaseInactiveSelected";
887 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
893 MidiTimeAxisView::toggle_step_edit ()
895 if (_route->record_enabled()) {
899 if (midi_track()->step_editing ()) {
900 stop_step_editing ();
902 start_step_editing ();
907 MidiTimeAxisView::start_step_editing ()
909 step_edit_insert_position = _editor.get_preferred_edit_position ();
910 step_edit_beat_pos = -1.0;
911 step_edit_region = playlist()->top_region_at (step_edit_insert_position);
913 if (step_edit_region) {
914 RegionView* rv = view()->find_view (step_edit_region);
915 step_edit_region_view = dynamic_cast<MidiRegionView*> (rv);
917 step_edit_region_view = 0;
920 midi_track()->set_step_editing (true);
924 MidiTimeAxisView::stop_step_editing ()
926 midi_track()->set_step_editing (false);
930 MidiTimeAxisView::check_step_edit ()
932 MidiRingBuffer<nframes_t>& incoming (midi_track()->step_edit_ring_buffer());
934 uint32_t bufsize = 32;
936 buf = new uint8_t[bufsize];
938 while (incoming.read_space()) {
940 Evoral::EventType type;
943 incoming.read_prefix (&time, &type, &size);
945 if (size > bufsize) {
948 buf = new uint8_t[bufsize];
951 incoming.read_contents (size, buf);
953 if ((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) {
955 if (step_edit_region == 0) {
957 step_edit_region = add_region (step_edit_insert_position);
958 RegionView* rv = view()->find_view (step_edit_region);
959 step_edit_region_view = dynamic_cast<MidiRegionView*>(rv);
962 if (step_edit_region && step_edit_region_view) {
964 if (step_edit_beat_pos < 0.0) {
965 framecnt_t frames_from_start = _editor.get_preferred_edit_position() - step_edit_region->position();
966 if (frames_from_start < 0) {
969 step_edit_beat_pos = step_edit_region_view->frames_to_beats (frames_from_start);
973 Evoral::MusicalTime beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
979 step_edit_region_view->step_add_note (buf[0] & 0xf, buf[1], buf[2], step_edit_beat_pos, beats);
980 step_edit_beat_pos += beats;
988 MidiTimeAxisView::step_edit_rest ()
991 Evoral::MusicalTime beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
992 step_edit_beat_pos += beats;
995 boost::shared_ptr<Region>
996 MidiTimeAxisView::add_region (framepos_t pos)
998 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1000 real_editor->begin_reversible_command (_("create region"));
1001 playlist()->clear_history ();
1003 framepos_t start = pos;
1004 real_editor->snap_to (start, -1);
1005 const Meter& m = _session->tempo_map().meter_at(start);
1006 const Tempo& t = _session->tempo_map().tempo_at(start);
1007 double length = floor (m.frames_per_bar(t, _session->frame_rate()));
1009 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1010 view()->trackview().track()->name());
1013 plist.add (ARDOUR::Properties::start, 0);
1014 plist.add (ARDOUR::Properties::length, length);
1015 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1017 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1019 playlist()->add_region (region, start);
1020 _session->add_command (new StatefulDiffCommand (playlist()));
1022 real_editor->commit_reversible_command();
1028 MidiTimeAxisView::add_note_selection (uint8_t note)
1030 if (!_editor.internal_editing()) {
1034 uint16_t chn_mask = _channel_selector.get_selected_channels();
1036 if (_view->num_selected_regionviews() == 0) {
1037 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1039 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1044 MidiTimeAxisView::extend_note_selection (uint8_t note)
1046 if (!_editor.internal_editing()) {
1050 uint16_t chn_mask = _channel_selector.get_selected_channels();
1052 if (_view->num_selected_regionviews() == 0) {
1053 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1055 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1060 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1062 if (!_editor.internal_editing()) {
1066 uint16_t chn_mask = _channel_selector.get_selected_channels();
1068 if (_view->num_selected_regionviews() == 0) {
1069 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1071 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1076 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1078 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1082 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1084 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1088 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1090 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1094 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1096 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1100 uint16_t selected_channels = _channel_selector.get_selected_channels();
1101 bool changed = false;
1105 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1107 for (uint32_t chn = 0; chn < 16; ++chn) {
1108 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1109 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1115 if ((selected_channels & (0x0001 << chn)) == 0) {
1116 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1117 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1119 changed = track->set_visibility (false) || changed;
1121 changed = track->set_visibility (true) || changed;
1128 /* TODO: Bender, PgmChange, Pressure */
1130 /* invalidate the controller menu, so that we rebuilt it next time */
1131 _controller_menu_map.clear ();
1132 delete controller_menu;
1133 controller_menu = 0;
1136 _route->gui_changed ("track_height", this);
1141 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1143 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1148 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1149 if (i != _controller_menu_map.end()) {
1153 i = _channel_command_menu_map.find (param);
1154 if (i != _channel_command_menu_map.end()) {