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"
84 #include "step_entry.h"
87 #include "ardour/midi_track.h"
91 using namespace ARDOUR;
94 using namespace Gtkmm2ext;
95 using namespace Editing;
97 // Minimum height at which a control is displayed
98 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
99 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
101 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
102 boost::shared_ptr<Route> rt, Canvas& canvas)
103 : AxisView(sess) // virtually inherited
104 , RouteTimeAxisView(ed, sess, rt, canvas)
105 , _ignore_signals(false)
107 , _piano_roll_header(0)
108 , _note_mode(Sustained)
110 , _percussion_mode_item(0)
111 , _color_mode(MeterColors)
112 , _meter_color_mode_item(0)
113 , _channel_color_mode_item(0)
114 , _track_color_mode_item(0)
115 , _step_edit_item (0)
116 , _midi_thru_item (0)
117 , default_channel_menu (0)
118 , controller_menu (0)
120 subplugin_menu.set_name ("ArdourContextMenu");
122 _view = new MidiStreamView (*this);
124 ignore_toggle = false;
126 mute_button->set_active (false);
127 solo_button->set_active (false);
129 step_edit_insert_position = 0;
131 if (is_midi_track()) {
132 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
133 _note_mode = midi_track()->note_mode();
134 } else { // MIDI bus (which doesn't exist yet..)
135 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
138 /* map current state of the route */
140 processors_changed (RouteProcessorChange ());
144 set_state (*xml_node, Stateful::loading_state_version);
146 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
149 _piano_roll_header = new PianoRollHeader(*midi_view());
151 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
152 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
153 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
155 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
157 controls_hbox.pack_start(*_range_scroomer);
158 controls_hbox.pack_start(*_piano_roll_header);
160 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
161 controls_base_selected_name = "MidiTrackControlsBaseSelected";
162 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
164 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
166 /* ask for notifications of any new RegionViews */
167 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
170 midi_track()->PlaylistChanged.connect (*this, invalidator (*this),
171 boost::bind (&MidiTimeAxisView::playlist_changed, this),
177 HBox* midi_controls_hbox = manage(new HBox());
179 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
181 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
182 for (; m != patch_manager.all_models().end(); ++m) {
183 _model_selector.append_text(m->c_str());
186 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
188 _custom_device_mode_selector.signal_changed().connect(
189 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
191 // TODO: persist the choice
192 // this initializes the comboboxes and sends out the signal
193 _model_selector.set_active(0);
195 midi_controls_hbox->pack_start(_channel_selector, true, false);
196 if (!patch_manager.all_models().empty()) {
197 _midi_controls_box.pack_start(_model_selector, true, false);
198 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
201 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
203 controls_vbox.pack_start(_midi_controls_box, false, false);
205 // restore channel selector settings
206 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
207 _channel_selector.mode_changed.connect(
208 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
209 _channel_selector.mode_changed.connect(
210 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
213 if ((prop = xml_node->property ("color-mode")) != 0) {
214 _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
215 if (_color_mode == ChannelColors) {
216 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
220 if ((prop = xml_node->property ("note-mode")) != 0) {
221 _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
222 if (_percussion_mode_item) {
223 _percussion_mode_item->set_active (_note_mode == Percussive);
228 MidiTimeAxisView::~MidiTimeAxisView ()
230 delete _piano_roll_header;
231 _piano_roll_header = 0;
233 delete _range_scroomer;
236 delete controller_menu;
240 MidiTimeAxisView::playlist_changed ()
242 step_edit_region_connection.disconnect ();
243 midi_track()->playlist()->RegionRemoved.connect (step_edit_region_connection, invalidator (*this),
244 ui_bind (&MidiTimeAxisView::region_removed, this, _1),
249 MidiTimeAxisView::region_removed (boost::weak_ptr<Region> wr)
251 boost::shared_ptr<Region> r (wr.lock());
257 if (step_edit_region == r) {
258 step_edit_region.reset();
259 // force a recompute of the insert position
260 step_edit_beat_pos = -1.0;
264 void MidiTimeAxisView::model_changed()
266 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
267 .custom_device_mode_names_by_model(_model_selector.get_active_text());
269 _custom_device_mode_selector.clear_items();
271 for (std::list<std::string>::const_iterator i = device_modes.begin();
272 i != device_modes.end(); ++i) {
273 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
274 _custom_device_mode_selector.append_text(*i);
277 _custom_device_mode_selector.set_active(0);
280 void MidiTimeAxisView::custom_device_mode_changed()
282 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
283 _custom_device_mode_selector.get_active_text());
287 MidiTimeAxisView::midi_view()
289 return dynamic_cast<MidiStreamView*>(_view);
293 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
296 xml_node->add_property ("shown-editor", "yes");
298 guint32 ret = TimeAxisView::show_at (y, nth, parent);
303 MidiTimeAxisView::hide ()
306 xml_node->add_property ("shown-editor", "no");
308 TimeAxisView::hide ();
312 MidiTimeAxisView::set_height (uint32_t h)
314 RouteTimeAxisView::set_height (h);
316 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
317 _midi_controls_box.show_all ();
319 _midi_controls_box.hide();
322 if (height >= KEYBOARD_MIN_HEIGHT) {
323 if (is_track() && _range_scroomer)
324 _range_scroomer->show();
325 if (is_track() && _piano_roll_header)
326 _piano_roll_header->show();
328 if (is_track() && _range_scroomer)
329 _range_scroomer->hide();
330 if (is_track() && _piano_roll_header)
331 _piano_roll_header->hide();
336 MidiTimeAxisView::append_extra_display_menu_items ()
338 using namespace Menu_Helpers;
340 MenuList& items = display_menu->items();
343 Menu *range_menu = manage(new Menu);
344 MenuList& range_items = range_menu->items();
345 range_menu->set_name ("ArdourContextMenu");
347 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
348 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
349 MidiStreamView::FullRange)));
351 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
352 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
353 MidiStreamView::ContentsRange)));
355 items.push_back (MenuElem (_("Note range"), *range_menu));
356 items.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
357 items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
359 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
360 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
364 MidiTimeAxisView::build_def_channel_menu ()
366 using namespace Menu_Helpers;
368 default_channel_menu = manage (new Menu ());
370 uint8_t defchn = midi_track()->default_channel();
371 MenuList& def_channel_items = default_channel_menu->items();
373 RadioMenuItem::Group dc_group;
375 for (int i = 0; i < 16; ++i) {
377 snprintf (buf, sizeof (buf), "%d", i+1);
379 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
380 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
381 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
382 item->set_active ((i == defchn));
385 return default_channel_menu;
389 MidiTimeAxisView::set_default_channel (int chn)
391 midi_track()->set_default_channel (chn);
395 MidiTimeAxisView::toggle_midi_thru ()
397 if (!_midi_thru_item) {
401 bool view_yn = _midi_thru_item->get_active();
402 if (view_yn != midi_track()->midi_thru()) {
403 midi_track()->set_midi_thru (view_yn);
408 MidiTimeAxisView::build_automation_action_menu ()
410 using namespace Menu_Helpers;
412 /* If we have a controller menu, we need to detach it before
413 RouteTimeAxis::build_automation_action_menu destroys the
414 menu it is attached to. Otherwise GTK destroys
415 controller_menu's gobj, meaning that it can't be reattached
416 below. See bug #3134.
419 if (controller_menu) {
420 detach_menu (*controller_menu);
423 _channel_command_menu_map.clear ();
424 RouteTimeAxisView::build_automation_action_menu ();
426 MenuList& automation_items = automation_action_menu->items();
428 uint16_t selected_channels = _channel_selector.get_selected_channels();
430 if (selected_channels != 0) {
432 automation_items.push_back (SeparatorElem());
434 /* these 3 MIDI "command" types are semantically more like automation than note data,
435 but they are not MIDI controllers. We give them special status in this menu, since
436 they will not show up in the controller list and anyone who actually knows
437 something about MIDI (!) would not expect to find them there.
440 add_channel_command_menu_item (automation_items, _("Program Change"), MidiPgmChangeAutomation, 0);
441 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
442 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
444 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
445 since it might need to be updated after a channel mode change or other change. Also detach it
446 first in case it has been used anywhere else.
449 build_controller_menu ();
451 automation_items.push_back (SeparatorElem());
452 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
454 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
460 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
462 uint16_t selected_channels = _channel_selector.get_selected_channels();
464 for (uint8_t chn = 0; chn < 16; chn++) {
465 if (selected_channels & (0x0001 << chn)) {
467 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
468 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
471 menu->set_active (yn);
478 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
480 using namespace Menu_Helpers;
482 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
485 uint16_t selected_channels = _channel_selector.get_selected_channels();
488 for (uint8_t chn = 0; chn < 16; chn++) {
489 if (selected_channels & (0x0001 << chn)) {
498 /* multiple channels - create a submenu, with 1 item per channel */
500 Menu* chn_menu = manage (new Menu);
501 MenuList& chn_items (chn_menu->items());
502 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
504 /* add a couple of items to hide/show all of them */
506 chn_items.push_back (MenuElem (_("Hide all channels"),
507 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
508 false, param_without_channel)));
509 chn_items.push_back (MenuElem (_("Show all channels"),
510 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
511 true, param_without_channel)));
513 for (uint8_t chn = 0; chn < 16; chn++) {
514 if (selected_channels & (0x0001 << chn)) {
516 /* for each selected channel, add a menu item for this controller */
518 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
519 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
520 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
521 fully_qualified_param)));
523 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
524 bool visible = false;
527 if (track->marked_for_display()) {
532 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
533 _channel_command_menu_map[fully_qualified_param] = cmi;
534 cmi->set_active (visible);
538 /* now create an item in the parent menu that has the per-channel list as a submenu */
540 items.push_back (MenuElem (label, *chn_menu));
544 /* just one channel - create a single menu item for this command+channel combination*/
546 for (uint8_t chn = 0; chn < 16; chn++) {
547 if (selected_channels & (0x0001 << chn)) {
549 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
550 items.push_back (CheckMenuElem (label,
551 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
552 fully_qualified_param)));
554 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
555 bool visible = false;
558 if (track->marked_for_display()) {
563 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
564 _channel_command_menu_map[fully_qualified_param] = cmi;
565 cmi->set_active (visible);
567 /* one channel only */
575 MidiTimeAxisView::build_controller_menu ()
577 using namespace Menu_Helpers;
579 if (controller_menu) {
580 /* it exists and has not been invalidated by a channel mode change, so just return it */
584 controller_menu = new Menu; // explicitly managed by us
585 MenuList& items (controller_menu->items());
587 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
588 for each controller+channel combination covering the currently selected channels for this track
591 uint16_t selected_channels = _channel_selector.get_selected_channels();
593 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
598 for (uint8_t chn = 0; chn < 16; chn++) {
599 if (selected_channels & (0x0001 << chn)) {
606 /* loop over all 127 MIDI controllers, in groups of 16 */
608 for (int i = 0; i < 127; i += 16) {
610 Menu* ctl_menu = manage (new Menu);
611 MenuList& ctl_items (ctl_menu->items());
614 /* for each controller, consider whether to create a submenu or a single item */
616 for (int ctl = i; ctl < i+16; ++ctl) {
620 /* multiple channels - create a submenu, with 1 item per channel */
622 Menu* chn_menu = manage (new Menu);
623 MenuList& chn_items (chn_menu->items());
625 /* add a couple of items to hide/show this controller on all channels */
627 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
628 chn_items.push_back (MenuElem (_("Hide all channels"),
629 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
630 false, param_without_channel)));
631 chn_items.push_back (MenuElem (_("Show all channels"),
632 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
633 true, param_without_channel)));
635 for (uint8_t chn = 0; chn < 16; chn++) {
636 if (selected_channels & (0x0001 << chn)) {
638 /* for each selected channel, add a menu item for this controller */
640 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
641 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
642 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
643 fully_qualified_param)));
645 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
646 bool visible = false;
649 if (track->marked_for_display()) {
654 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
655 _controller_menu_map[fully_qualified_param] = cmi;
656 cmi->set_active (visible);
660 /* add the per-channel menu to the list of controllers, with the name of the controller */
661 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
662 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
666 /* just one channel - create a single menu item for this ctl+channel combination*/
668 for (uint8_t chn = 0; chn < 16; chn++) {
669 if (selected_channels & (0x0001 << chn)) {
671 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
672 ctl_items.push_back (CheckMenuElem (_route->describe_parameter (fully_qualified_param),
673 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
674 fully_qualified_param)));
676 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
677 bool visible = false;
680 if (track->marked_for_display()) {
685 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
686 _controller_menu_map[fully_qualified_param] = cmi;
687 cmi->set_active (visible);
689 /* one channel only */
696 /* add the menu for this block of controllers to the overall controller menu */
698 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
703 MidiTimeAxisView::build_note_mode_menu()
705 using namespace Menu_Helpers;
707 Menu* mode_menu = manage (new Menu);
708 MenuList& items = mode_menu->items();
709 mode_menu->set_name ("ArdourContextMenu");
711 RadioMenuItem::Group mode_group;
712 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
713 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
714 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
715 _note_mode_item->set_active(_note_mode == Sustained);
717 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
718 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
719 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
720 _percussion_mode_item->set_active(_note_mode == Percussive);
726 MidiTimeAxisView::build_color_mode_menu()
728 using namespace Menu_Helpers;
730 Menu* mode_menu = manage (new Menu);
731 MenuList& items = mode_menu->items();
732 mode_menu->set_name ("ArdourContextMenu");
734 RadioMenuItem::Group mode_group;
735 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
736 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), MeterColors)));
737 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
738 _meter_color_mode_item->set_active(_color_mode == MeterColors);
740 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
741 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
742 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
743 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
745 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
746 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
747 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
748 _channel_color_mode_item->set_active(_color_mode == TrackColor);
754 MidiTimeAxisView::set_note_mode(NoteMode mode)
756 if (_note_mode != mode || midi_track()->note_mode() != mode) {
758 midi_track()->set_note_mode(mode);
759 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
760 _view->redisplay_track();
765 MidiTimeAxisView::set_color_mode(ColorMode mode)
767 if (_color_mode != mode) {
768 if (mode == ChannelColors) {
769 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
771 _channel_selector.set_default_channel_color();
775 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
776 _view->redisplay_track();
781 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
783 if (!_ignore_signals)
784 midi_view()->set_note_range(range);
789 MidiTimeAxisView::update_range()
791 MidiGhostRegion* mgr;
793 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
794 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
801 MidiTimeAxisView::show_all_automation ()
804 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
806 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
807 create_automation_child(*i, true);
811 RouteTimeAxisView::show_all_automation ();
815 MidiTimeAxisView::show_existing_automation ()
818 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
820 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
821 create_automation_child(*i, true);
825 RouteTimeAxisView::show_existing_automation ();
828 /** Create an automation track for the given parameter (pitch bend, channel pressure).
831 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
833 /* These controllers are region "automation", so we do not create
834 * an AutomationList/Line for the track */
836 if (param.type() == NullAutomation) {
837 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
841 AutomationTracks::iterator existing = _automation_tracks.find (param);
842 if (existing != _automation_tracks.end()) {
846 boost::shared_ptr<AutomationControl> c = _route->get_control (param);
850 boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session,
851 _route, boost::shared_ptr<ARDOUR::Automatable>(), c,
856 _route->describe_parameter(param)));
858 add_automation_child (param, track, show);
863 MidiTimeAxisView::route_active_changed ()
865 RouteUI::route_active_changed ();
868 if (_route->active()) {
869 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
870 controls_base_selected_name = "MidiTrackControlsBaseSelected";
871 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
873 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
874 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
875 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
881 if (_route->active()) {
882 controls_ebox.set_name ("BusControlsBaseUnselected");
883 controls_base_selected_name = "BusControlsBaseSelected";
884 controls_base_unselected_name = "BusControlsBaseUnselected";
886 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
887 controls_base_selected_name = "BusControlsBaseInactiveSelected";
888 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
894 MidiTimeAxisView::toggle_step_edit ()
896 if (_route->record_enabled()) {
900 if (midi_track()->step_editing ()) {
901 stop_step_editing ();
903 start_step_editing ();
908 MidiTimeAxisView::start_step_editing ()
910 step_edit_insert_position = _editor.get_preferred_edit_position ();
911 step_edit_beat_pos = -1.0;
912 _step_edit_triplet_countdown = 0;
913 _step_edit_within_chord = 0;
914 _step_edit_chord_duration = 0.0;
916 step_edit_region = playlist()->top_region_at (step_edit_insert_position);
918 if (step_edit_region) {
919 RegionView* rv = view()->find_view (step_edit_region);
920 step_edit_region_view = dynamic_cast<MidiRegionView*> (rv);
922 step_edit_region_view = 0;
925 midi_track()->set_step_editing (true);
927 StepEntry* se = new StepEntry (*this);
929 se->set_position (WIN_POS_MOUSE);
934 MidiTimeAxisView::stop_step_editing ()
936 midi_track()->set_step_editing (false);
940 MidiTimeAxisView::check_step_edit ()
942 MidiRingBuffer<nframes_t>& incoming (midi_track()->step_edit_ring_buffer());
944 uint32_t bufsize = 32;
946 buf = new uint8_t[bufsize];
948 while (incoming.read_space()) {
950 Evoral::EventType type;
953 incoming.read_prefix (&time, &type, &size);
955 if (size > bufsize) {
958 buf = new uint8_t[bufsize];
961 incoming.read_contents (size, buf);
963 if ((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) {
964 step_add_note (buf[0] & 0xf, buf[1], buf[2], 0.0);
970 MidiTimeAxisView::step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, Evoral::MusicalTime beat_duration)
972 if (step_edit_region == 0) {
974 step_edit_region = add_region (step_edit_insert_position);
975 RegionView* rv = view()->find_view (step_edit_region);
976 step_edit_region_view = dynamic_cast<MidiRegionView*>(rv);
979 if (step_edit_region && step_edit_region_view) {
980 if (step_edit_beat_pos < 0.0) {
981 framecnt_t frames_from_start = _editor.get_preferred_edit_position() - step_edit_region->position();
982 if (frames_from_start < 0) {
985 step_edit_beat_pos = step_edit_region_view->frames_to_beats (frames_from_start);
988 if (beat_duration == 0.0) {
990 beat_duration = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
997 step_edit_region_view->step_add_note (channel, pitch, velocity, step_edit_beat_pos, beat_duration);
999 if (_step_edit_triplet_countdown > 0) {
1000 _step_edit_triplet_countdown--;
1002 if (_step_edit_triplet_countdown == 0) {
1003 _step_edit_triplet_countdown = 3;
1007 if (!_step_edit_within_chord) {
1008 step_edit_beat_pos += beat_duration;
1010 step_edit_beat_pos += 1.0/Meter::ticks_per_beat; // tiny, but no longer overlapping
1011 _step_edit_chord_duration = beat_duration;
1019 MidiTimeAxisView::step_edit_within_triplet() const
1021 return _step_edit_triplet_countdown > 0;
1025 MidiTimeAxisView::step_edit_within_chord() const
1027 return _step_edit_within_chord;
1031 MidiTimeAxisView::step_edit_toggle_triplet ()
1033 if (_step_edit_triplet_countdown == 0) {
1034 _step_edit_within_chord = false;
1035 _step_edit_triplet_countdown = 3;
1037 _step_edit_triplet_countdown = 0;
1042 MidiTimeAxisView::step_edit_toggle_chord ()
1044 if (_step_edit_within_chord) {
1045 _step_edit_within_chord = false;
1046 step_edit_beat_pos += _step_edit_chord_duration;
1048 _step_edit_triplet_countdown = 0;
1049 _step_edit_within_chord = true;
1054 MidiTimeAxisView::step_edit_rest ()
1057 Evoral::MusicalTime beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
1058 step_edit_beat_pos += beats;
1061 boost::shared_ptr<Region>
1062 MidiTimeAxisView::add_region (framepos_t pos)
1064 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1066 real_editor->begin_reversible_command (_("create region"));
1067 playlist()->clear_history ();
1069 framepos_t start = pos;
1070 real_editor->snap_to (start, -1);
1071 const Meter& m = _session->tempo_map().meter_at(start);
1072 const Tempo& t = _session->tempo_map().tempo_at(start);
1073 double length = floor (m.frames_per_bar(t, _session->frame_rate()));
1075 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1076 view()->trackview().track()->name());
1079 plist.add (ARDOUR::Properties::start, 0);
1080 plist.add (ARDOUR::Properties::length, length);
1081 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1083 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1085 playlist()->add_region (region, start);
1086 _session->add_command (new StatefulDiffCommand (playlist()));
1088 real_editor->commit_reversible_command();
1094 MidiTimeAxisView::add_note_selection (uint8_t note)
1096 if (!_editor.internal_editing()) {
1100 uint16_t chn_mask = _channel_selector.get_selected_channels();
1102 if (_view->num_selected_regionviews() == 0) {
1103 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1105 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1110 MidiTimeAxisView::extend_note_selection (uint8_t note)
1112 if (!_editor.internal_editing()) {
1116 uint16_t chn_mask = _channel_selector.get_selected_channels();
1118 if (_view->num_selected_regionviews() == 0) {
1119 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1121 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1126 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1128 if (!_editor.internal_editing()) {
1132 uint16_t chn_mask = _channel_selector.get_selected_channels();
1134 if (_view->num_selected_regionviews() == 0) {
1135 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1137 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1142 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1144 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1148 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1150 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1154 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1156 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1160 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1162 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1166 uint16_t selected_channels = _channel_selector.get_selected_channels();
1167 bool changed = false;
1171 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1173 for (uint32_t chn = 0; chn < 16; ++chn) {
1174 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1175 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1181 if ((selected_channels & (0x0001 << chn)) == 0) {
1182 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1183 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1185 changed = track->set_visibility (false) || changed;
1187 changed = track->set_visibility (true) || changed;
1194 /* TODO: Bender, PgmChange, Pressure */
1196 /* invalidate the controller menu, so that we rebuilt it next time */
1197 _controller_menu_map.clear ();
1198 delete controller_menu;
1199 controller_menu = 0;
1202 _route->gui_changed ("track_height", this);
1207 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1209 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1214 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1215 if (i != _controller_menu_map.end()) {
1219 i = _channel_command_menu_map.find (param);
1220 if (i != _channel_command_menu_map.end()) {