use -r argument with JACK if realtime is not requested in engine dialog (also applied...
[ArdourMidi.git] / gtk2_ardour / midi_time_axis.cc
bloba4e09871c92b0e681f16d1468858e6280b6c7cbb
1 /*
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.
19 #include <cstdlib>
20 #include <cmath>
22 #include <algorithm>
23 #include <string>
24 #include <vector>
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"
65 #include "editor.h"
66 #include "enums.h"
67 #include "ghostregion.h"
68 #include "gui_thread.h"
69 #include "keyboard.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"
79 #include "prompter.h"
80 #include "region_view.h"
81 #include "rgb_macros.h"
82 #include "selection.h"
83 #include "simplerect.h"
84 #include "utils.h"
86 #include "ardour/midi_track.h"
88 #include "i18n.h"
90 using namespace ARDOUR;
91 using namespace PBD;
92 using namespace Gtk;
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)
105 , _range_scroomer(0)
106 , _piano_roll_header(0)
107 , _note_mode(Sustained)
108 , _note_mode_item(0)
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 ());
141 ensure_xml_node ();
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());
147 if (is_track()) {
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));
167 _view->attach ();
170 HBox* midi_controls_hbox = manage(new HBox());
172 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
174 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
175 for (; m != patch_manager.all_models().end(); ++m) {
176 _model_selector.append_text(m->c_str());
179 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
181 _custom_device_mode_selector.signal_changed().connect(
182 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
184 // TODO: persist the choice
185 // this initializes the comboboxes and sends out the signal
186 _model_selector.set_active(0);
188 midi_controls_hbox->pack_start(_channel_selector, true, false);
189 if (!patch_manager.all_models().empty()) {
190 _midi_controls_box.pack_start(_model_selector, true, false);
191 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
194 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
196 controls_vbox.pack_start(_midi_controls_box, false, false);
198 // restore channel selector settings
199 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
200 _channel_selector.mode_changed.connect(
201 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
202 _channel_selector.mode_changed.connect(
203 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
205 XMLProperty *prop;
206 if ((prop = xml_node->property ("color-mode")) != 0) {
207 _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
208 if (_color_mode == ChannelColors) {
209 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
213 if ((prop = xml_node->property ("note-mode")) != 0) {
214 _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
215 if (_percussion_mode_item) {
216 _percussion_mode_item->set_active (_note_mode == Percussive);
221 MidiTimeAxisView::~MidiTimeAxisView ()
223 delete _piano_roll_header;
224 _piano_roll_header = 0;
226 delete _range_scroomer;
227 _range_scroomer = 0;
229 delete controller_menu;
232 void MidiTimeAxisView::model_changed()
234 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
235 .custom_device_mode_names_by_model(_model_selector.get_active_text());
237 _custom_device_mode_selector.clear_items();
239 for (std::list<std::string>::const_iterator i = device_modes.begin();
240 i != device_modes.end(); ++i) {
241 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
242 _custom_device_mode_selector.append_text(*i);
245 _custom_device_mode_selector.set_active(0);
248 void MidiTimeAxisView::custom_device_mode_changed()
250 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
251 _custom_device_mode_selector.get_active_text());
254 MidiStreamView*
255 MidiTimeAxisView::midi_view()
257 return dynamic_cast<MidiStreamView*>(_view);
260 guint32
261 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
263 ensure_xml_node ();
264 xml_node->add_property ("shown-editor", "yes");
266 guint32 ret = TimeAxisView::show_at (y, nth, parent);
267 return ret;
270 void
271 MidiTimeAxisView::hide ()
273 ensure_xml_node ();
274 xml_node->add_property ("shown-editor", "no");
276 TimeAxisView::hide ();
279 void
280 MidiTimeAxisView::set_height (uint32_t h)
282 RouteTimeAxisView::set_height (h);
284 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
285 _midi_controls_box.show_all ();
286 } else {
287 _midi_controls_box.hide();
290 if (height >= KEYBOARD_MIN_HEIGHT) {
291 if (is_track() && _range_scroomer)
292 _range_scroomer->show();
293 if (is_track() && _piano_roll_header)
294 _piano_roll_header->show();
295 } else {
296 if (is_track() && _range_scroomer)
297 _range_scroomer->hide();
298 if (is_track() && _piano_roll_header)
299 _piano_roll_header->hide();
303 void
304 MidiTimeAxisView::append_extra_display_menu_items ()
306 using namespace Menu_Helpers;
308 MenuList& items = display_menu->items();
310 // Note range
311 Menu *range_menu = manage(new Menu);
312 MenuList& range_items = range_menu->items();
313 range_menu->set_name ("ArdourContextMenu");
315 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
316 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
317 MidiStreamView::FullRange)));
319 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
320 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
321 MidiStreamView::ContentsRange)));
323 items.push_back (MenuElem (_("Note range"), *range_menu));
324 items.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
325 items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
327 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
328 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
331 Gtk::Menu*
332 MidiTimeAxisView::build_def_channel_menu ()
334 using namespace Menu_Helpers;
336 default_channel_menu = manage (new Menu ());
338 uint8_t defchn = midi_track()->default_channel();
339 MenuList& def_channel_items = default_channel_menu->items();
340 RadioMenuItem* item;
341 RadioMenuItem::Group dc_group;
343 for (int i = 0; i < 16; ++i) {
344 char buf[4];
345 snprintf (buf, sizeof (buf), "%d", i+1);
347 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
348 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
349 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
350 item->set_active ((i == defchn));
353 return default_channel_menu;
356 void
357 MidiTimeAxisView::set_default_channel (int chn)
359 midi_track()->set_default_channel (chn);
362 void
363 MidiTimeAxisView::toggle_midi_thru ()
365 if (!_midi_thru_item) {
366 return;
369 bool view_yn = _midi_thru_item->get_active();
370 if (view_yn != midi_track()->midi_thru()) {
371 midi_track()->set_midi_thru (view_yn);
375 void
376 MidiTimeAxisView::build_automation_action_menu ()
378 using namespace Menu_Helpers;
380 /* If we have a controller menu, we need to detach it before
381 RouteTimeAxis::build_automation_action_menu destroys the
382 menu it is attached to. Otherwise GTK destroys
383 controller_menu's gobj, meaning that it can't be reattached
384 below. See bug #3134.
387 if (controller_menu) {
388 detach_menu (*controller_menu);
391 _channel_command_menu_map.clear ();
392 RouteTimeAxisView::build_automation_action_menu ();
394 MenuList& automation_items = automation_action_menu->items();
396 uint16_t selected_channels = _channel_selector.get_selected_channels();
398 if (selected_channels != 0) {
400 automation_items.push_back (SeparatorElem());
402 /* these 3 MIDI "command" types are semantically more like automation than note data,
403 but they are not MIDI controllers. We give them special status in this menu, since
404 they will not show up in the controller list and anyone who actually knows
405 something about MIDI (!) would not expect to find them there.
408 add_channel_command_menu_item (automation_items, _("Program Change"), MidiPgmChangeAutomation, 0);
409 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
410 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
412 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
413 since it might need to be updated after a channel mode change or other change. Also detach it
414 first in case it has been used anywhere else.
417 build_controller_menu ();
419 automation_items.push_back (SeparatorElem());
420 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
421 } else {
422 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
427 void
428 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
430 uint16_t selected_channels = _channel_selector.get_selected_channels();
432 for (uint8_t chn = 0; chn < 16; chn++) {
433 if (selected_channels & (0x0001 << chn)) {
435 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
436 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
438 if (menu) {
439 menu->set_active (yn);
445 void
446 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
448 using namespace Menu_Helpers;
450 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
453 uint16_t selected_channels = _channel_selector.get_selected_channels();
454 int chn_cnt = 0;
456 for (uint8_t chn = 0; chn < 16; chn++) {
457 if (selected_channels & (0x0001 << chn)) {
458 if (++chn_cnt > 1) {
459 break;
464 if (chn_cnt > 1) {
466 /* multiple channels - create a submenu, with 1 item per channel */
468 Menu* chn_menu = manage (new Menu);
469 MenuList& chn_items (chn_menu->items());
470 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
472 /* add a couple of items to hide/show all of them */
474 chn_items.push_back (MenuElem (_("Hide all channels"),
475 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
476 false, param_without_channel)));
477 chn_items.push_back (MenuElem (_("Show all channels"),
478 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
479 true, param_without_channel)));
481 for (uint8_t chn = 0; chn < 16; chn++) {
482 if (selected_channels & (0x0001 << chn)) {
484 /* for each selected channel, add a menu item for this controller */
486 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
487 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
488 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
489 fully_qualified_param)));
491 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
492 bool visible = false;
494 if (track) {
495 if (track->marked_for_display()) {
496 visible = true;
500 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
501 _channel_command_menu_map[fully_qualified_param] = cmi;
502 cmi->set_active (visible);
506 /* now create an item in the parent menu that has the per-channel list as a submenu */
508 items.push_back (MenuElem (label, *chn_menu));
510 } else {
512 /* just one channel - create a single menu item for this command+channel combination*/
514 for (uint8_t chn = 0; chn < 16; chn++) {
515 if (selected_channels & (0x0001 << chn)) {
517 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
518 items.push_back (CheckMenuElem (label,
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;
525 if (track) {
526 if (track->marked_for_display()) {
527 visible = true;
531 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
532 _channel_command_menu_map[fully_qualified_param] = cmi;
533 cmi->set_active (visible);
535 /* one channel only */
536 break;
542 void
543 MidiTimeAxisView::build_controller_menu ()
545 using namespace Menu_Helpers;
547 if (controller_menu) {
548 /* it exists and has not been invalidated by a channel mode change, so just return it */
549 return;
552 controller_menu = new Menu; // explicitly managed by us
553 MenuList& items (controller_menu->items());
555 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
556 for each controller+channel combination covering the currently selected channels for this track
559 uint16_t selected_channels = _channel_selector.get_selected_channels();
561 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
564 int chn_cnt = 0;
566 for (uint8_t chn = 0; chn < 16; chn++) {
567 if (selected_channels & (0x0001 << chn)) {
568 if (++chn_cnt > 1) {
569 break;
574 /* loop over all 127 MIDI controllers, in groups of 16 */
576 for (int i = 0; i < 127; i += 16) {
578 Menu* ctl_menu = manage (new Menu);
579 MenuList& ctl_items (ctl_menu->items());
582 /* for each controller, consider whether to create a submenu or a single item */
584 for (int ctl = i; ctl < i+16; ++ctl) {
586 if (chn_cnt > 1) {
588 /* multiple channels - create a submenu, with 1 item per channel */
590 Menu* chn_menu = manage (new Menu);
591 MenuList& chn_items (chn_menu->items());
593 /* add a couple of items to hide/show this controller on all channels */
595 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
596 chn_items.push_back (MenuElem (_("Hide all channels"),
597 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
598 false, param_without_channel)));
599 chn_items.push_back (MenuElem (_("Show all channels"),
600 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
601 true, param_without_channel)));
603 for (uint8_t chn = 0; chn < 16; chn++) {
604 if (selected_channels & (0x0001 << chn)) {
606 /* for each selected channel, add a menu item for this controller */
608 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
609 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
610 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
611 fully_qualified_param)));
613 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
614 bool visible = false;
616 if (track) {
617 if (track->marked_for_display()) {
618 visible = true;
622 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
623 _controller_menu_map[fully_qualified_param] = cmi;
624 cmi->set_active (visible);
628 /* add the per-channel menu to the list of controllers, with the name of the controller */
629 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
630 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
632 } else {
634 /* just one channel - create a single menu item for this ctl+channel combination*/
636 for (uint8_t chn = 0; chn < 16; chn++) {
637 if (selected_channels & (0x0001 << chn)) {
639 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
640 ctl_items.push_back (CheckMenuElem (_route->describe_parameter (fully_qualified_param),
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;
647 if (track) {
648 if (track->marked_for_display()) {
649 visible = true;
653 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
654 _controller_menu_map[fully_qualified_param] = cmi;
655 cmi->set_active (visible);
657 /* one channel only */
658 break;
664 /* add the menu for this block of controllers to the overall controller menu */
666 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
670 Gtk::Menu*
671 MidiTimeAxisView::build_note_mode_menu()
673 using namespace Menu_Helpers;
675 Menu* mode_menu = manage (new Menu);
676 MenuList& items = mode_menu->items();
677 mode_menu->set_name ("ArdourContextMenu");
679 RadioMenuItem::Group mode_group;
680 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
681 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
682 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
683 _note_mode_item->set_active(_note_mode == Sustained);
685 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
686 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
687 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
688 _percussion_mode_item->set_active(_note_mode == Percussive);
690 return mode_menu;
693 Gtk::Menu*
694 MidiTimeAxisView::build_color_mode_menu()
696 using namespace Menu_Helpers;
698 Menu* mode_menu = manage (new Menu);
699 MenuList& items = mode_menu->items();
700 mode_menu->set_name ("ArdourContextMenu");
702 RadioMenuItem::Group mode_group;
703 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
704 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), MeterColors)));
705 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
706 _meter_color_mode_item->set_active(_color_mode == MeterColors);
708 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
709 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
710 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
711 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
713 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
714 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
715 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
716 _channel_color_mode_item->set_active(_color_mode == TrackColor);
718 return mode_menu;
721 void
722 MidiTimeAxisView::set_note_mode(NoteMode mode)
724 if (_note_mode != mode || midi_track()->note_mode() != mode) {
725 _note_mode = mode;
726 midi_track()->set_note_mode(mode);
727 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
728 _view->redisplay_track();
732 void
733 MidiTimeAxisView::set_color_mode(ColorMode mode)
735 if (_color_mode != mode) {
736 if (mode == ChannelColors) {
737 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
738 } else {
739 _channel_selector.set_default_channel_color();
742 _color_mode = mode;
743 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
744 _view->redisplay_track();
748 void
749 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
751 if (!_ignore_signals)
752 midi_view()->set_note_range(range);
756 void
757 MidiTimeAxisView::update_range()
759 MidiGhostRegion* mgr;
761 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
762 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
763 mgr->update_range();
768 void
769 MidiTimeAxisView::show_all_automation ()
771 if (midi_track()) {
772 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
774 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
775 create_automation_child(*i, true);
779 RouteTimeAxisView::show_all_automation ();
782 void
783 MidiTimeAxisView::show_existing_automation ()
785 if (midi_track()) {
786 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
788 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
789 create_automation_child(*i, true);
793 RouteTimeAxisView::show_existing_automation ();
796 /** Create an automation track for the given parameter (pitch bend, channel pressure).
798 void
799 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
801 /* These controllers are region "automation", so we do not create
802 * an AutomationList/Line for the track */
804 if (param.type() == NullAutomation) {
805 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
806 return;
809 AutomationTracks::iterator existing = _automation_tracks.find (param);
810 if (existing != _automation_tracks.end()) {
811 return;
814 boost::shared_ptr<AutomationControl> c = _route->get_control (param);
816 assert(c);
818 boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session,
819 _route, boost::shared_ptr<ARDOUR::Automatable>(), c,
820 _editor,
821 *this,
822 true,
823 parent_canvas,
824 _route->describe_parameter(param)));
826 add_automation_child (param, track, show);
830 void
831 MidiTimeAxisView::route_active_changed ()
833 RouteUI::route_active_changed ();
835 if (is_track()) {
836 if (_route->active()) {
837 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
838 controls_base_selected_name = "MidiTrackControlsBaseSelected";
839 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
840 } else {
841 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
842 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
843 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
845 } else {
847 throw; // wha?
849 if (_route->active()) {
850 controls_ebox.set_name ("BusControlsBaseUnselected");
851 controls_base_selected_name = "BusControlsBaseSelected";
852 controls_base_unselected_name = "BusControlsBaseUnselected";
853 } else {
854 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
855 controls_base_selected_name = "BusControlsBaseInactiveSelected";
856 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
861 void
862 MidiTimeAxisView::start_step_editing ()
864 step_edit_insert_position = _editor.get_preferred_edit_position ();
865 step_edit_beat_pos = 0;
866 step_edit_region = playlist()->top_region_at (step_edit_insert_position);
868 if (step_edit_region) {
869 RegionView* rv = view()->find_view (step_edit_region);
870 step_edit_region_view = dynamic_cast<MidiRegionView*> (rv);
871 } else {
872 step_edit_region_view = 0;
875 midi_track()->set_step_editing (true);
878 void
879 MidiTimeAxisView::stop_step_editing ()
881 midi_track()->set_step_editing (false);
884 void
885 MidiTimeAxisView::check_step_edit ()
887 MidiRingBuffer<nframes_t>& incoming (midi_track()->step_edit_ring_buffer());
888 uint8_t* buf;
889 uint32_t bufsize = 32;
891 buf = new uint8_t[bufsize];
893 while (incoming.read_space()) {
894 nframes_t time;
895 Evoral::EventType type;
896 uint32_t size;
898 incoming.read_prefix (&time, &type, &size);
900 if (size > bufsize) {
901 delete [] buf;
902 bufsize = size;
903 buf = new uint8_t[bufsize];
906 incoming.read_contents (size, buf);
908 if ((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) {
910 if (step_edit_region == 0) {
912 step_edit_region = add_region (step_edit_insert_position);
913 RegionView* rv = view()->find_view (step_edit_region);
915 if (rv) {
916 step_edit_region_view = dynamic_cast<MidiRegionView*>(rv);
917 } else {
918 fatal << X_("programming error: no view found for new MIDI region") << endmsg;
919 /*NOTREACHED*/
923 if (step_edit_region_view) {
925 bool success;
926 Evoral::MusicalTime beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
928 if (!success) {
929 continue;
932 step_edit_region_view->add_note (buf[0] & 0xf, buf[1], buf[2], step_edit_beat_pos, beats);
933 step_edit_beat_pos += beats;
940 void
941 MidiTimeAxisView::step_edit_rest ()
943 bool success;
944 Evoral::MusicalTime beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
945 step_edit_beat_pos += beats;
948 boost::shared_ptr<Region>
949 MidiTimeAxisView::add_region (nframes64_t pos)
951 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
953 real_editor->begin_reversible_command (_("create region"));
954 playlist()->clear_history ();
956 framepos_t start = pos;
957 real_editor->snap_to (start, -1);
958 const Meter& m = _session->tempo_map().meter_at(start);
959 const Tempo& t = _session->tempo_map().tempo_at(start);
960 double length = floor (m.frames_per_bar(t, _session->frame_rate()));
962 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
963 view()->trackview().track()->name());
964 PropertyList plist;
966 plist.add (ARDOUR::Properties::start, 0);
967 plist.add (ARDOUR::Properties::length, length);
968 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
970 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
972 playlist()->add_region (region, start);
973 _session->add_command (new StatefulDiffCommand (playlist()));
975 real_editor->commit_reversible_command();
977 return region;
980 void
981 MidiTimeAxisView::add_note_selection (uint8_t note)
983 if (!_editor.internal_editing()) {
984 return;
987 uint16_t chn_mask = _channel_selector.get_selected_channels();
989 if (_view->num_selected_regionviews() == 0) {
990 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
991 } else {
992 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
996 void
997 MidiTimeAxisView::extend_note_selection (uint8_t note)
999 if (!_editor.internal_editing()) {
1000 return;
1003 uint16_t chn_mask = _channel_selector.get_selected_channels();
1005 if (_view->num_selected_regionviews() == 0) {
1006 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1007 } else {
1008 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1012 void
1013 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1015 if (!_editor.internal_editing()) {
1016 return;
1019 uint16_t chn_mask = _channel_selector.get_selected_channels();
1021 if (_view->num_selected_regionviews() == 0) {
1022 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1023 } else {
1024 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1028 void
1029 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1031 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1034 void
1035 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1037 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1040 void
1041 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1043 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1046 void
1047 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1049 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1050 the right ones.
1053 uint16_t selected_channels = _channel_selector.get_selected_channels();
1054 bool changed = false;
1056 no_redraw = true;
1058 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1060 for (uint32_t chn = 0; chn < 16; ++chn) {
1061 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1062 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1064 if (!track) {
1065 continue;
1068 if ((selected_channels & (0x0001 << chn)) == 0) {
1069 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1070 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1072 changed = track->set_visibility (false) || changed;
1073 } else {
1074 changed = track->set_visibility (true) || changed;
1079 no_redraw = false;
1081 /* TODO: Bender, PgmChange, Pressure */
1083 /* invalidate the controller menu, so that we rebuilt it next time */
1084 _controller_menu_map.clear ();
1085 delete controller_menu;
1086 controller_menu = 0;
1088 if (changed) {
1089 _route->gui_changed ("track_height", this);
1093 Gtk::CheckMenuItem*
1094 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1096 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1097 if (m) {
1098 return m;
1101 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1102 if (i != _controller_menu_map.end()) {
1103 return i->second;
1106 i = _channel_command_menu_map.find (param);
1107 if (i != _channel_command_menu_map.end()) {
1108 return i->second;
1111 return 0;