fix math bug with numthreads computation
[ardour2.git] / gtk2_ardour / midi_time_axis.cc
blob9479d2aeb88fc1192494d24c754809426374ccda
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/midi_playlist.h"
42 #include "ardour/midi_diskstream.h"
43 #include "ardour/midi_patch_manager.h"
44 #include "ardour/midi_source.h"
45 #include "ardour/processor.h"
46 #include "ardour/ladspa_plugin.h"
47 #include "ardour/location.h"
48 #include "ardour/playlist.h"
49 #include "ardour/region_factory.h"
50 #include "ardour/session.h"
51 #include "ardour/session_playlist.h"
52 #include "ardour/tempo.h"
53 #include "ardour/utils.h"
55 #include "midi++/names.h"
57 #include "add_midi_cc_track_dialog.h"
58 #include "ardour_ui.h"
59 #include "automation_line.h"
60 #include "automation_time_axis.h"
61 #include "canvas-note-event.h"
62 #include "canvas_impl.h"
63 #include "crossfade_view.h"
64 #include "editor.h"
65 #include "enums.h"
66 #include "ghostregion.h"
67 #include "gui_thread.h"
68 #include "keyboard.h"
69 #include "midi_scroomer.h"
70 #include "midi_streamview.h"
71 #include "midi_region_view.h"
72 #include "midi_time_axis.h"
73 #include "piano_roll_header.h"
74 #include "playlist_selector.h"
75 #include "plugin_selector.h"
76 #include "plugin_ui.h"
77 #include "point_selection.h"
78 #include "prompter.h"
79 #include "region_view.h"
80 #include "rgb_macros.h"
81 #include "selection.h"
82 #include "simplerect.h"
83 #include "utils.h"
85 #include "ardour/midi_track.h"
87 #include "i18n.h"
89 using namespace ARDOUR;
90 using namespace PBD;
91 using namespace Gtk;
92 using namespace Gtkmm2ext;
93 using namespace Editing;
95 // Minimum height at which a control is displayed
96 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
97 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
99 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
100 boost::shared_ptr<Route> rt, Canvas& canvas)
101 : AxisView(sess) // virtually inherited
102 , RouteTimeAxisView(ed, sess, rt, canvas)
103 , _ignore_signals(false)
104 , _range_scroomer(0)
105 , _piano_roll_header(0)
106 , _note_mode(Sustained)
107 , _note_mode_item(0)
108 , _percussion_mode_item(0)
109 , _color_mode(MeterColors)
110 , _meter_color_mode_item(0)
111 , _channel_color_mode_item(0)
112 , _track_color_mode_item(0)
113 , _step_edit_item (0)
114 , _midi_thru_item (0)
115 , default_channel_menu (0)
116 , controller_menu (0)
118 subplugin_menu.set_name ("ArdourContextMenu");
120 _view = new MidiStreamView (*this);
122 ignore_toggle = false;
124 mute_button->set_active (false);
125 solo_button->set_active (false);
127 step_edit_insert_position = 0;
129 if (is_midi_track()) {
130 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
131 _note_mode = midi_track()->note_mode();
132 } else { // MIDI bus (which doesn't exist yet..)
133 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
136 /* map current state of the route */
138 processors_changed (RouteProcessorChange ());
140 ensure_xml_node ();
142 set_state (*xml_node, Stateful::loading_state_version);
144 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
146 if (is_track()) {
147 _piano_roll_header = new PianoRollHeader(*midi_view());
149 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
150 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
151 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
153 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
155 controls_hbox.pack_start(*_range_scroomer);
156 controls_hbox.pack_start(*_piano_roll_header);
158 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
159 controls_base_selected_name = "MidiTrackControlsBaseSelected";
160 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
162 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
164 /* ask for notifications of any new RegionViews */
165 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
166 _view->attach ();
169 HBox* midi_controls_hbox = manage(new HBox());
171 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
173 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
174 for (; m != patch_manager.all_models().end(); ++m) {
175 _model_selector.append_text(m->c_str());
178 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
180 _custom_device_mode_selector.signal_changed().connect(
181 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
183 // TODO: persist the choice
184 // this initializes the comboboxes and sends out the signal
185 _model_selector.set_active(0);
187 midi_controls_hbox->pack_start(_channel_selector, true, false);
188 if (!patch_manager.all_models().empty()) {
189 _midi_controls_box.pack_start(_model_selector, true, false);
190 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
193 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
195 controls_vbox.pack_start(_midi_controls_box, false, false);
197 // restore channel selector settings
198 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
199 _channel_selector.mode_changed.connect(
200 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
201 _channel_selector.mode_changed.connect(
202 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
204 XMLProperty *prop;
205 if ((prop = xml_node->property ("color-mode")) != 0) {
206 _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
207 if (_color_mode == ChannelColors) {
208 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
212 if ((prop = xml_node->property ("note-mode")) != 0) {
213 _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
214 if (_percussion_mode_item) {
215 _percussion_mode_item->set_active (_note_mode == Percussive);
220 MidiTimeAxisView::~MidiTimeAxisView ()
222 delete _piano_roll_header;
223 _piano_roll_header = 0;
225 delete _range_scroomer;
226 _range_scroomer = 0;
228 delete controller_menu;
231 void MidiTimeAxisView::model_changed()
233 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
234 .custom_device_mode_names_by_model(_model_selector.get_active_text());
236 _custom_device_mode_selector.clear_items();
238 for (std::list<std::string>::const_iterator i = device_modes.begin();
239 i != device_modes.end(); ++i) {
240 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
241 _custom_device_mode_selector.append_text(*i);
244 _custom_device_mode_selector.set_active(0);
247 void MidiTimeAxisView::custom_device_mode_changed()
249 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
250 _custom_device_mode_selector.get_active_text());
253 MidiStreamView*
254 MidiTimeAxisView::midi_view()
256 return dynamic_cast<MidiStreamView*>(_view);
259 guint32
260 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
262 ensure_xml_node ();
263 xml_node->add_property ("shown-editor", "yes");
265 guint32 ret = TimeAxisView::show_at (y, nth, parent);
266 return ret;
269 void
270 MidiTimeAxisView::hide ()
272 ensure_xml_node ();
273 xml_node->add_property ("shown-editor", "no");
275 TimeAxisView::hide ();
278 void
279 MidiTimeAxisView::set_height (uint32_t h)
281 RouteTimeAxisView::set_height (h);
283 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
284 _midi_controls_box.show_all ();
285 } else {
286 _midi_controls_box.hide();
289 if (height >= KEYBOARD_MIN_HEIGHT) {
290 if (is_track() && _range_scroomer)
291 _range_scroomer->show();
292 if (is_track() && _piano_roll_header)
293 _piano_roll_header->show();
294 } else {
295 if (is_track() && _range_scroomer)
296 _range_scroomer->hide();
297 if (is_track() && _piano_roll_header)
298 _piano_roll_header->hide();
302 void
303 MidiTimeAxisView::append_extra_display_menu_items ()
305 using namespace Menu_Helpers;
307 MenuList& items = display_menu->items();
309 // Note range
310 Menu *range_menu = manage(new Menu);
311 MenuList& range_items = range_menu->items();
312 range_menu->set_name ("ArdourContextMenu");
314 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
315 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
316 MidiStreamView::FullRange)));
318 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
319 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
320 MidiStreamView::ContentsRange)));
322 items.push_back (MenuElem (_("Note range"), *range_menu));
323 items.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
324 items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
326 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
327 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
330 Gtk::Menu*
331 MidiTimeAxisView::build_def_channel_menu ()
333 using namespace Menu_Helpers;
335 default_channel_menu = manage (new Menu ());
337 uint8_t defchn = midi_track()->default_channel();
338 MenuList& def_channel_items = default_channel_menu->items();
339 RadioMenuItem* item;
340 RadioMenuItem::Group dc_group;
342 for (int i = 0; i < 16; ++i) {
343 char buf[4];
344 snprintf (buf, sizeof (buf), "%d", i+1);
346 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
347 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
348 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
349 item->set_active ((i == defchn));
352 return default_channel_menu;
355 void
356 MidiTimeAxisView::set_default_channel (int chn)
358 midi_track()->set_default_channel (chn);
361 void
362 MidiTimeAxisView::toggle_midi_thru ()
364 if (!_midi_thru_item) {
365 return;
368 bool view_yn = _midi_thru_item->get_active();
369 if (view_yn != midi_track()->midi_thru()) {
370 midi_track()->set_midi_thru (view_yn);
374 void
375 MidiTimeAxisView::build_automation_action_menu ()
377 using namespace Menu_Helpers;
379 /* If we have a controller menu, we need to detach it before
380 RouteTimeAxis::build_automation_action_menu destroys the
381 menu it is attached to. Otherwise GTK destroys
382 controller_menu's gobj, meaning that it can't be reattached
383 below. See bug #3134.
386 if (controller_menu) {
387 detach_menu (*controller_menu);
390 _channel_command_menu_map.clear ();
391 RouteTimeAxisView::build_automation_action_menu ();
393 MenuList& automation_items = automation_action_menu->items();
395 uint16_t selected_channels = _channel_selector.get_selected_channels();
397 if (selected_channels != 0) {
399 automation_items.push_back (SeparatorElem());
401 /* these 3 MIDI "command" types are semantically more like automation than note data,
402 but they are not MIDI controllers. We give them special status in this menu, since
403 they will not show up in the controller list and anyone who actually knows
404 something about MIDI (!) would not expect to find them there.
407 add_channel_command_menu_item (automation_items, _("Program Change"), MidiPgmChangeAutomation, 0);
408 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
409 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
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 } else {
421 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
426 void
427 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
429 uint16_t selected_channels = _channel_selector.get_selected_channels();
431 for (uint8_t chn = 0; chn < 16; chn++) {
432 if (selected_channels & (0x0001 << chn)) {
434 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
435 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
437 if (menu) {
438 menu->set_active (yn);
444 void
445 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
447 using namespace Menu_Helpers;
449 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
452 uint16_t selected_channels = _channel_selector.get_selected_channels();
453 int chn_cnt = 0;
455 for (uint8_t chn = 0; chn < 16; chn++) {
456 if (selected_channels & (0x0001 << chn)) {
457 if (++chn_cnt > 1) {
458 break;
463 if (chn_cnt > 1) {
465 /* multiple channels - create a submenu, with 1 item per channel */
467 Menu* chn_menu = manage (new Menu);
468 MenuList& chn_items (chn_menu->items());
469 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
471 /* add a couple of items to hide/show all of them */
473 chn_items.push_back (MenuElem (_("Hide all channels"),
474 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
475 false, param_without_channel)));
476 chn_items.push_back (MenuElem (_("Show all channels"),
477 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
478 true, param_without_channel)));
480 for (uint8_t chn = 0; chn < 16; chn++) {
481 if (selected_channels & (0x0001 << chn)) {
483 /* for each selected channel, add a menu item for this controller */
485 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
486 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
487 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
488 fully_qualified_param)));
490 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
491 bool visible = false;
493 if (track) {
494 if (track->marked_for_display()) {
495 visible = true;
499 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
500 _channel_command_menu_map[fully_qualified_param] = cmi;
501 cmi->set_active (visible);
505 /* now create an item in the parent menu that has the per-channel list as a submenu */
507 items.push_back (MenuElem (label, *chn_menu));
509 } else {
511 /* just one channel - create a single menu item for this command+channel combination*/
513 for (uint8_t chn = 0; chn < 16; chn++) {
514 if (selected_channels & (0x0001 << chn)) {
516 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
517 items.push_back (CheckMenuElem (label,
518 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
519 fully_qualified_param)));
521 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
522 bool visible = false;
524 if (track) {
525 if (track->marked_for_display()) {
526 visible = true;
530 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
531 _channel_command_menu_map[fully_qualified_param] = cmi;
532 cmi->set_active (visible);
534 /* one channel only */
535 break;
541 void
542 MidiTimeAxisView::build_controller_menu ()
544 using namespace Menu_Helpers;
546 if (controller_menu) {
547 /* it exists and has not been invalidated by a channel mode change, so just return it */
548 return;
551 controller_menu = new Menu; // explicitly managed by us
552 MenuList& items (controller_menu->items());
554 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
555 for each controller+channel combination covering the currently selected channels for this track
558 uint16_t selected_channels = _channel_selector.get_selected_channels();
560 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
563 int chn_cnt = 0;
565 for (uint8_t chn = 0; chn < 16; chn++) {
566 if (selected_channels & (0x0001 << chn)) {
567 if (++chn_cnt > 1) {
568 break;
573 /* loop over all 127 MIDI controllers, in groups of 16 */
575 for (int i = 0; i < 127; i += 16) {
577 Menu* ctl_menu = manage (new Menu);
578 MenuList& ctl_items (ctl_menu->items());
581 /* for each controller, consider whether to create a submenu or a single item */
583 for (int ctl = i; ctl < i+16; ++ctl) {
585 if (chn_cnt > 1) {
587 /* multiple channels - create a submenu, with 1 item per channel */
589 Menu* chn_menu = manage (new Menu);
590 MenuList& chn_items (chn_menu->items());
592 /* add a couple of items to hide/show this controller on all channels */
594 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
595 chn_items.push_back (MenuElem (_("Hide all channels"),
596 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
597 false, param_without_channel)));
598 chn_items.push_back (MenuElem (_("Show all channels"),
599 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
600 true, param_without_channel)));
602 for (uint8_t chn = 0; chn < 16; chn++) {
603 if (selected_channels & (0x0001 << chn)) {
605 /* for each selected channel, add a menu item for this controller */
607 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
608 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
609 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
610 fully_qualified_param)));
612 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
613 bool visible = false;
615 if (track) {
616 if (track->marked_for_display()) {
617 visible = true;
621 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
622 _controller_menu_map[fully_qualified_param] = cmi;
623 cmi->set_active (visible);
627 /* add the per-channel menu to the list of controllers, with the name of the controller */
628 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
629 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
631 } else {
633 /* just one channel - create a single menu item for this ctl+channel combination*/
635 for (uint8_t chn = 0; chn < 16; chn++) {
636 if (selected_channels & (0x0001 << chn)) {
638 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
639 ctl_items.push_back (CheckMenuElem (_route->describe_parameter (fully_qualified_param),
640 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
641 fully_qualified_param)));
643 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
644 bool visible = false;
646 if (track) {
647 if (track->marked_for_display()) {
648 visible = true;
652 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
653 _controller_menu_map[fully_qualified_param] = cmi;
654 cmi->set_active (visible);
656 /* one channel only */
657 break;
663 /* add the menu for this block of controllers to the overall controller menu */
665 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
669 Gtk::Menu*
670 MidiTimeAxisView::build_note_mode_menu()
672 using namespace Menu_Helpers;
674 Menu* mode_menu = manage (new Menu);
675 MenuList& items = mode_menu->items();
676 mode_menu->set_name ("ArdourContextMenu");
678 RadioMenuItem::Group mode_group;
679 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
680 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
681 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
682 _note_mode_item->set_active(_note_mode == Sustained);
684 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
685 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
686 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
687 _percussion_mode_item->set_active(_note_mode == Percussive);
689 return mode_menu;
692 Gtk::Menu*
693 MidiTimeAxisView::build_color_mode_menu()
695 using namespace Menu_Helpers;
697 Menu* mode_menu = manage (new Menu);
698 MenuList& items = mode_menu->items();
699 mode_menu->set_name ("ArdourContextMenu");
701 RadioMenuItem::Group mode_group;
702 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
703 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), MeterColors)));
704 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
705 _meter_color_mode_item->set_active(_color_mode == MeterColors);
707 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
708 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
709 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
710 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
712 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
713 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
714 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
715 _channel_color_mode_item->set_active(_color_mode == TrackColor);
717 return mode_menu;
720 void
721 MidiTimeAxisView::set_note_mode(NoteMode mode)
723 if (_note_mode != mode || midi_track()->note_mode() != mode) {
724 _note_mode = mode;
725 midi_track()->set_note_mode(mode);
726 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
727 _view->redisplay_track();
731 void
732 MidiTimeAxisView::set_color_mode(ColorMode mode)
734 if (_color_mode != mode) {
735 if (mode == ChannelColors) {
736 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
737 } else {
738 _channel_selector.set_default_channel_color();
741 _color_mode = mode;
742 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
743 _view->redisplay_track();
747 void
748 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
750 if (!_ignore_signals)
751 midi_view()->set_note_range(range);
755 void
756 MidiTimeAxisView::update_range()
758 MidiGhostRegion* mgr;
760 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
761 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
762 mgr->update_range();
767 void
768 MidiTimeAxisView::show_all_automation ()
770 if (midi_track()) {
771 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
773 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
774 create_automation_child(*i, true);
778 RouteTimeAxisView::show_all_automation ();
781 void
782 MidiTimeAxisView::show_existing_automation ()
784 if (midi_track()) {
785 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
787 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
788 create_automation_child(*i, true);
792 RouteTimeAxisView::show_existing_automation ();
795 /** Create an automation track for the given parameter (pitch bend, channel pressure).
797 void
798 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
800 /* These controllers are region "automation", so we do not create
801 * an AutomationList/Line for the track */
803 if (param.type() == NullAutomation) {
804 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
805 return;
808 AutomationTracks::iterator existing = _automation_tracks.find (param);
809 if (existing != _automation_tracks.end()) {
810 return;
813 boost::shared_ptr<AutomationControl> c = _route->get_control (param);
815 assert(c);
817 boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session,
818 _route, boost::shared_ptr<ARDOUR::Automatable>(), c,
819 _editor,
820 *this,
821 true,
822 parent_canvas,
823 _route->describe_parameter(param)));
825 add_automation_child (param, track, show);
829 void
830 MidiTimeAxisView::route_active_changed ()
832 RouteUI::route_active_changed ();
834 if (is_track()) {
835 if (_route->active()) {
836 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
837 controls_base_selected_name = "MidiTrackControlsBaseSelected";
838 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
839 } else {
840 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
841 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
842 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
844 } else {
846 throw; // wha?
848 if (_route->active()) {
849 controls_ebox.set_name ("BusControlsBaseUnselected");
850 controls_base_selected_name = "BusControlsBaseSelected";
851 controls_base_unselected_name = "BusControlsBaseUnselected";
852 } else {
853 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
854 controls_base_selected_name = "BusControlsBaseInactiveSelected";
855 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
860 void
861 MidiTimeAxisView::start_step_editing ()
863 step_edit_insert_position = _editor.get_preferred_edit_position ();
864 step_edit_beat_pos = 0;
865 step_edit_region = playlist()->top_region_at (step_edit_insert_position);
867 if (step_edit_region) {
868 RegionView* rv = view()->find_view (step_edit_region);
869 step_edit_region_view = dynamic_cast<MidiRegionView*> (rv);
870 } else {
871 step_edit_region_view = 0;
874 midi_track()->set_step_editing (true);
877 void
878 MidiTimeAxisView::stop_step_editing ()
880 midi_track()->set_step_editing (false);
883 void
884 MidiTimeAxisView::check_step_edit ()
886 MidiRingBuffer<nframes_t>& incoming (midi_track()->step_edit_ring_buffer());
887 Evoral::Note<Evoral::MusicalTime> note;
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());
965 PropertyList plist;
967 plist.add (ARDOUR::Properties::start, 0);
968 plist.add (ARDOUR::Properties::length, length);
969 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
971 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
973 playlist()->add_region (region, start);
974 _session->add_command (new StatefulDiffCommand (playlist()));
976 real_editor->commit_reversible_command();
978 return region;
981 void
982 MidiTimeAxisView::add_note_selection (uint8_t note)
984 if (!_editor.internal_editing()) {
985 return;
988 uint16_t chn_mask = _channel_selector.get_selected_channels();
990 if (_view->num_selected_regionviews() == 0) {
991 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
992 } else {
993 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
997 void
998 MidiTimeAxisView::extend_note_selection (uint8_t note)
1000 if (!_editor.internal_editing()) {
1001 return;
1004 uint16_t chn_mask = _channel_selector.get_selected_channels();
1006 if (_view->num_selected_regionviews() == 0) {
1007 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1008 } else {
1009 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1013 void
1014 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1016 if (!_editor.internal_editing()) {
1017 return;
1020 uint16_t chn_mask = _channel_selector.get_selected_channels();
1022 if (_view->num_selected_regionviews() == 0) {
1023 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1024 } else {
1025 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1029 void
1030 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1032 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1035 void
1036 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1038 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1041 void
1042 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1044 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1047 void
1048 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1050 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1051 the right ones.
1054 uint16_t selected_channels = _channel_selector.get_selected_channels();
1055 bool changed = false;
1057 no_redraw = true;
1059 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1061 for (uint32_t chn = 0; chn < 16; ++chn) {
1062 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1063 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1065 if (!track) {
1066 continue;
1069 if ((selected_channels & (0x0001 << chn)) == 0) {
1070 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1071 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1073 changed = track->set_visibility (false) || changed;
1074 } else {
1075 changed = track->set_visibility (true) || changed;
1080 no_redraw = false;
1082 /* TODO: Bender, PgmChange, Pressure */
1084 /* invalidate the controller menu, so that we rebuilt it next time */
1085 _controller_menu_map.clear ();
1086 delete controller_menu;
1087 controller_menu = 0;
1089 if (changed) {
1090 _route->gui_changed ("track_height", this);
1094 Gtk::CheckMenuItem*
1095 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1097 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1098 if (m) {
1099 return m;
1102 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1103 if (i != _controller_menu_map.end()) {
1104 return i->second;
1107 i = _channel_command_menu_map.find (param);
1108 if (i != _channel_command_menu_map.end()) {
1109 return i->second;
1112 return 0;