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)
121 subplugin_menu.set_name ("ArdourContextMenu");
123 _view = new MidiStreamView (*this);
125 ignore_toggle = false;
127 mute_button->set_active (false);
128 solo_button->set_active (false);
130 step_edit_insert_position = 0;
132 if (is_midi_track()) {
133 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
134 _note_mode = midi_track()->note_mode();
135 } else { // MIDI bus (which doesn't exist yet..)
136 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
139 /* map current state of the route */
141 processors_changed (RouteProcessorChange ());
145 set_state (*xml_node, Stateful::loading_state_version);
147 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
150 _piano_roll_header = new PianoRollHeader(*midi_view());
152 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
153 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
154 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
156 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
158 controls_hbox.pack_start(*_range_scroomer);
159 controls_hbox.pack_start(*_piano_roll_header);
161 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
162 controls_base_selected_name = "MidiTrackControlsBaseSelected";
163 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
165 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
167 /* ask for notifications of any new RegionViews */
168 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
171 midi_track()->PlaylistChanged.connect (*this, invalidator (*this),
172 boost::bind (&MidiTimeAxisView::playlist_changed, this),
178 HBox* midi_controls_hbox = manage(new HBox());
180 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
182 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
183 for (; m != patch_manager.all_models().end(); ++m) {
184 _model_selector.append_text(m->c_str());
187 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
189 _custom_device_mode_selector.signal_changed().connect(
190 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
192 // TODO: persist the choice
193 // this initializes the comboboxes and sends out the signal
194 _model_selector.set_active(0);
196 midi_controls_hbox->pack_start(_channel_selector, true, false);
197 if (!patch_manager.all_models().empty()) {
198 _midi_controls_box.pack_start(_model_selector, true, false);
199 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
202 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
204 controls_vbox.pack_start(_midi_controls_box, false, false);
206 // restore channel selector settings
207 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
208 _channel_selector.mode_changed.connect(
209 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
210 _channel_selector.mode_changed.connect(
211 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
214 if ((prop = xml_node->property ("color-mode")) != 0) {
215 _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
216 if (_color_mode == ChannelColors) {
217 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
221 if ((prop = xml_node->property ("note-mode")) != 0) {
222 _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
223 if (_percussion_mode_item) {
224 _percussion_mode_item->set_active (_note_mode == Percussive);
229 MidiTimeAxisView::~MidiTimeAxisView ()
231 delete _piano_roll_header;
232 _piano_roll_header = 0;
234 delete _range_scroomer;
237 delete controller_menu;
241 MidiTimeAxisView::playlist_changed ()
243 step_edit_region_connection.disconnect ();
244 midi_track()->playlist()->RegionRemoved.connect (step_edit_region_connection, invalidator (*this),
245 ui_bind (&MidiTimeAxisView::region_removed, this, _1),
250 MidiTimeAxisView::region_removed (boost::weak_ptr<Region> wr)
252 boost::shared_ptr<Region> r (wr.lock());
258 if (step_edit_region == r) {
259 step_edit_region.reset();
260 step_edit_region_view = 0;
261 // force a recompute of the insert position
262 step_edit_beat_pos = -1.0;
266 void MidiTimeAxisView::model_changed()
268 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
269 .custom_device_mode_names_by_model(_model_selector.get_active_text());
271 _custom_device_mode_selector.clear_items();
273 for (std::list<std::string>::const_iterator i = device_modes.begin();
274 i != device_modes.end(); ++i) {
275 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
276 _custom_device_mode_selector.append_text(*i);
279 _custom_device_mode_selector.set_active(0);
282 void MidiTimeAxisView::custom_device_mode_changed()
284 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
285 _custom_device_mode_selector.get_active_text());
289 MidiTimeAxisView::midi_view()
291 return dynamic_cast<MidiStreamView*>(_view);
295 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
298 xml_node->add_property ("shown-editor", "yes");
300 guint32 ret = TimeAxisView::show_at (y, nth, parent);
305 MidiTimeAxisView::hide ()
308 xml_node->add_property ("shown-editor", "no");
310 TimeAxisView::hide ();
314 MidiTimeAxisView::set_height (uint32_t h)
316 RouteTimeAxisView::set_height (h);
318 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
319 _midi_controls_box.show_all ();
321 _midi_controls_box.hide();
324 if (height >= KEYBOARD_MIN_HEIGHT) {
325 if (is_track() && _range_scroomer)
326 _range_scroomer->show();
327 if (is_track() && _piano_roll_header)
328 _piano_roll_header->show();
330 if (is_track() && _range_scroomer)
331 _range_scroomer->hide();
332 if (is_track() && _piano_roll_header)
333 _piano_roll_header->hide();
338 MidiTimeAxisView::append_extra_display_menu_items ()
340 using namespace Menu_Helpers;
342 MenuList& items = display_menu->items();
345 Menu *range_menu = manage(new Menu);
346 MenuList& range_items = range_menu->items();
347 range_menu->set_name ("ArdourContextMenu");
349 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
350 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
351 MidiStreamView::FullRange)));
353 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
354 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
355 MidiStreamView::ContentsRange)));
357 items.push_back (MenuElem (_("Note range"), *range_menu));
358 items.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
359 items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
361 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
362 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
366 MidiTimeAxisView::build_def_channel_menu ()
368 using namespace Menu_Helpers;
370 default_channel_menu = manage (new Menu ());
372 uint8_t defchn = midi_track()->default_channel();
373 MenuList& def_channel_items = default_channel_menu->items();
375 RadioMenuItem::Group dc_group;
377 for (int i = 0; i < 16; ++i) {
379 snprintf (buf, sizeof (buf), "%d", i+1);
381 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
382 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
383 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
384 item->set_active ((i == defchn));
387 return default_channel_menu;
391 MidiTimeAxisView::set_default_channel (int chn)
393 midi_track()->set_default_channel (chn);
397 MidiTimeAxisView::toggle_midi_thru ()
399 if (!_midi_thru_item) {
403 bool view_yn = _midi_thru_item->get_active();
404 if (view_yn != midi_track()->midi_thru()) {
405 midi_track()->set_midi_thru (view_yn);
410 MidiTimeAxisView::build_automation_action_menu ()
412 using namespace Menu_Helpers;
414 /* If we have a controller menu, we need to detach it before
415 RouteTimeAxis::build_automation_action_menu destroys the
416 menu it is attached to. Otherwise GTK destroys
417 controller_menu's gobj, meaning that it can't be reattached
418 below. See bug #3134.
421 if (controller_menu) {
422 detach_menu (*controller_menu);
425 _channel_command_menu_map.clear ();
426 RouteTimeAxisView::build_automation_action_menu ();
428 MenuList& automation_items = automation_action_menu->items();
430 uint16_t selected_channels = _channel_selector.get_selected_channels();
432 if (selected_channels != 0) {
434 automation_items.push_back (SeparatorElem());
436 /* these 3 MIDI "command" types are semantically more like automation than note data,
437 but they are not MIDI controllers. We give them special status in this menu, since
438 they will not show up in the controller list and anyone who actually knows
439 something about MIDI (!) would not expect to find them there.
442 add_channel_command_menu_item (automation_items, _("Program Change"), MidiPgmChangeAutomation, 0);
443 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
444 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
446 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
447 since it might need to be updated after a channel mode change or other change. Also detach it
448 first in case it has been used anywhere else.
451 build_controller_menu ();
453 automation_items.push_back (SeparatorElem());
454 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
456 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
462 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
464 uint16_t selected_channels = _channel_selector.get_selected_channels();
466 for (uint8_t chn = 0; chn < 16; chn++) {
467 if (selected_channels & (0x0001 << chn)) {
469 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
470 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
473 menu->set_active (yn);
480 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
482 using namespace Menu_Helpers;
484 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
487 uint16_t selected_channels = _channel_selector.get_selected_channels();
490 for (uint8_t chn = 0; chn < 16; chn++) {
491 if (selected_channels & (0x0001 << chn)) {
500 /* multiple channels - create a submenu, with 1 item per channel */
502 Menu* chn_menu = manage (new Menu);
503 MenuList& chn_items (chn_menu->items());
504 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
506 /* add a couple of items to hide/show all of them */
508 chn_items.push_back (MenuElem (_("Hide all channels"),
509 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
510 false, param_without_channel)));
511 chn_items.push_back (MenuElem (_("Show all channels"),
512 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
513 true, param_without_channel)));
515 for (uint8_t chn = 0; chn < 16; chn++) {
516 if (selected_channels & (0x0001 << chn)) {
518 /* for each selected channel, add a menu item for this controller */
520 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
521 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
522 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
523 fully_qualified_param)));
525 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
526 bool visible = false;
529 if (track->marked_for_display()) {
534 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
535 _channel_command_menu_map[fully_qualified_param] = cmi;
536 cmi->set_active (visible);
540 /* now create an item in the parent menu that has the per-channel list as a submenu */
542 items.push_back (MenuElem (label, *chn_menu));
546 /* just one channel - create a single menu item for this command+channel combination*/
548 for (uint8_t chn = 0; chn < 16; chn++) {
549 if (selected_channels & (0x0001 << chn)) {
551 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
552 items.push_back (CheckMenuElem (label,
553 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
554 fully_qualified_param)));
556 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
557 bool visible = false;
560 if (track->marked_for_display()) {
565 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
566 _channel_command_menu_map[fully_qualified_param] = cmi;
567 cmi->set_active (visible);
569 /* one channel only */
577 MidiTimeAxisView::build_controller_menu ()
579 using namespace Menu_Helpers;
581 if (controller_menu) {
582 /* it exists and has not been invalidated by a channel mode change, so just return it */
586 controller_menu = new Menu; // explicitly managed by us
587 MenuList& items (controller_menu->items());
589 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
590 for each controller+channel combination covering the currently selected channels for this track
593 uint16_t selected_channels = _channel_selector.get_selected_channels();
595 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
600 for (uint8_t chn = 0; chn < 16; chn++) {
601 if (selected_channels & (0x0001 << chn)) {
608 /* loop over all 127 MIDI controllers, in groups of 16 */
610 for (int i = 0; i < 127; i += 16) {
612 Menu* ctl_menu = manage (new Menu);
613 MenuList& ctl_items (ctl_menu->items());
616 /* for each controller, consider whether to create a submenu or a single item */
618 for (int ctl = i; ctl < i+16; ++ctl) {
622 /* multiple channels - create a submenu, with 1 item per channel */
624 Menu* chn_menu = manage (new Menu);
625 MenuList& chn_items (chn_menu->items());
627 /* add a couple of items to hide/show this controller on all channels */
629 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
630 chn_items.push_back (MenuElem (_("Hide all channels"),
631 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
632 false, param_without_channel)));
633 chn_items.push_back (MenuElem (_("Show all channels"),
634 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
635 true, param_without_channel)));
637 for (uint8_t chn = 0; chn < 16; chn++) {
638 if (selected_channels & (0x0001 << chn)) {
640 /* for each selected channel, add a menu item for this controller */
642 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
643 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
644 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
645 fully_qualified_param)));
647 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
648 bool visible = false;
651 if (track->marked_for_display()) {
656 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
657 _controller_menu_map[fully_qualified_param] = cmi;
658 cmi->set_active (visible);
662 /* add the per-channel menu to the list of controllers, with the name of the controller */
663 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
664 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
668 /* just one channel - create a single menu item for this ctl+channel combination*/
670 for (uint8_t chn = 0; chn < 16; chn++) {
671 if (selected_channels & (0x0001 << chn)) {
673 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
674 ctl_items.push_back (CheckMenuElem (_route->describe_parameter (fully_qualified_param),
675 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
676 fully_qualified_param)));
678 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
679 bool visible = false;
682 if (track->marked_for_display()) {
687 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
688 _controller_menu_map[fully_qualified_param] = cmi;
689 cmi->set_active (visible);
691 /* one channel only */
698 /* add the menu for this block of controllers to the overall controller menu */
700 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
705 MidiTimeAxisView::build_note_mode_menu()
707 using namespace Menu_Helpers;
709 Menu* mode_menu = manage (new Menu);
710 MenuList& items = mode_menu->items();
711 mode_menu->set_name ("ArdourContextMenu");
713 RadioMenuItem::Group mode_group;
714 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
715 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
716 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
717 _note_mode_item->set_active(_note_mode == Sustained);
719 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
720 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
721 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
722 _percussion_mode_item->set_active(_note_mode == Percussive);
728 MidiTimeAxisView::build_color_mode_menu()
730 using namespace Menu_Helpers;
732 Menu* mode_menu = manage (new Menu);
733 MenuList& items = mode_menu->items();
734 mode_menu->set_name ("ArdourContextMenu");
736 RadioMenuItem::Group mode_group;
737 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
738 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), MeterColors)));
739 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
740 _meter_color_mode_item->set_active(_color_mode == MeterColors);
742 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
743 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
744 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
745 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
747 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
748 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
749 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
750 _channel_color_mode_item->set_active(_color_mode == TrackColor);
756 MidiTimeAxisView::set_note_mode(NoteMode mode)
758 if (_note_mode != mode || midi_track()->note_mode() != mode) {
760 midi_track()->set_note_mode(mode);
761 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
762 _view->redisplay_track();
767 MidiTimeAxisView::set_color_mode(ColorMode mode)
769 if (_color_mode != mode) {
770 if (mode == ChannelColors) {
771 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
773 _channel_selector.set_default_channel_color();
777 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
778 _view->redisplay_track();
783 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
785 if (!_ignore_signals)
786 midi_view()->set_note_range(range);
791 MidiTimeAxisView::update_range()
793 MidiGhostRegion* mgr;
795 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
796 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
803 MidiTimeAxisView::show_all_automation ()
806 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
808 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
809 create_automation_child(*i, true);
813 RouteTimeAxisView::show_all_automation ();
817 MidiTimeAxisView::show_existing_automation ()
820 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
822 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
823 create_automation_child(*i, true);
827 RouteTimeAxisView::show_existing_automation ();
830 /** Create an automation track for the given parameter (pitch bend, channel pressure).
833 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
835 /* These controllers are region "automation", so we do not create
836 * an AutomationList/Line for the track */
838 if (param.type() == NullAutomation) {
839 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
843 AutomationTracks::iterator existing = _automation_tracks.find (param);
844 if (existing != _automation_tracks.end()) {
848 boost::shared_ptr<AutomationControl> c = _route->get_control (param);
852 boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session,
853 _route, boost::shared_ptr<ARDOUR::Automatable>(), c,
858 _route->describe_parameter(param)));
860 add_automation_child (param, track, show);
865 MidiTimeAxisView::route_active_changed ()
867 RouteUI::route_active_changed ();
870 if (_route->active()) {
871 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
872 controls_base_selected_name = "MidiTrackControlsBaseSelected";
873 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
875 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
876 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
877 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
883 if (_route->active()) {
884 controls_ebox.set_name ("BusControlsBaseUnselected");
885 controls_base_selected_name = "BusControlsBaseSelected";
886 controls_base_unselected_name = "BusControlsBaseUnselected";
888 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
889 controls_base_selected_name = "BusControlsBaseInactiveSelected";
890 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
896 MidiTimeAxisView::start_step_editing ()
898 _step_edit_triplet_countdown = 0;
899 _step_edit_within_chord = 0;
900 _step_edit_chord_duration = 0.0;
901 step_edit_region.reset ();
902 step_edit_region_view = 0;
904 resync_step_edit_position ();
905 prepare_step_edit_region ();
906 reset_step_edit_beat_pos ();
908 assert (step_edit_region);
909 assert (step_edit_region_view);
911 if (step_editor == 0) {
912 step_editor = new StepEntry (*this);
913 step_editor->signal_delete_event().connect (sigc::mem_fun (*this, &MidiTimeAxisView::step_editor_hidden));
914 step_editor->signal_hide().connect (sigc::mem_fun (*this, &MidiTimeAxisView::step_editor_hide));
917 step_edit_region_view->show_step_edit_cursor (step_edit_beat_pos);
918 step_edit_region_view->set_step_edit_cursor_width (step_editor->note_length());
920 step_editor->set_position (WIN_POS_MOUSE);
921 step_editor->present ();
925 MidiTimeAxisView::resync_step_edit_position ()
927 step_edit_insert_position = _editor.get_preferred_edit_position ();
931 MidiTimeAxisView::resync_step_edit_to_edit_point ()
933 resync_step_edit_position ();
934 if (step_edit_region) {
935 reset_step_edit_beat_pos ();
940 MidiTimeAxisView::prepare_step_edit_region ()
942 boost::shared_ptr<Region> r = playlist()->top_region_at (step_edit_insert_position);
945 step_edit_region = boost::dynamic_pointer_cast<MidiRegion>(r);
948 if (step_edit_region) {
949 RegionView* rv = view()->find_view (step_edit_region);
950 step_edit_region_view = dynamic_cast<MidiRegionView*> (rv);
953 step_edit_region = add_region (step_edit_insert_position);
954 RegionView* rv = view()->find_view (step_edit_region);
955 step_edit_region_view = dynamic_cast<MidiRegionView*>(rv);
961 MidiTimeAxisView::reset_step_edit_beat_pos ()
963 assert (step_edit_region);
964 assert (step_edit_region_view);
966 framecnt_t frames_from_start = _editor.get_preferred_edit_position() - step_edit_region->position();
968 if (frames_from_start < 0) {
969 /* this can happen with snap enabled, and the edit point == Playhead. we snap the
970 position of the new region, and it can end up after the edit point.
972 frames_from_start = 0;
975 step_edit_beat_pos = step_edit_region_view->frames_to_beats (frames_from_start);
976 step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
980 MidiTimeAxisView::step_editor_hidden (GdkEventAny*)
987 MidiTimeAxisView::step_editor_hide ()
989 /* everything else will follow the change in the model */
990 midi_track()->set_step_editing (false);
994 MidiTimeAxisView::stop_step_editing ()
997 step_editor->hide ();
1000 if (step_edit_region_view) {
1001 step_edit_region_view->hide_step_edit_cursor();
1004 step_edit_region.reset ();
1008 MidiTimeAxisView::check_step_edit ()
1010 MidiRingBuffer<nframes_t>& incoming (midi_track()->step_edit_ring_buffer());
1012 uint32_t bufsize = 32;
1014 buf = new uint8_t[bufsize];
1016 while (incoming.read_space()) {
1018 Evoral::EventType type;
1021 incoming.read_prefix (&time, &type, &size);
1023 if (size > bufsize) {
1026 buf = new uint8_t[bufsize];
1029 incoming.read_contents (size, buf);
1031 if ((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) {
1032 step_add_note (buf[0] & 0xf, buf[1], buf[2], 0.0);
1038 MidiTimeAxisView::step_add_bank_change (uint8_t channel, uint8_t bank)
1044 MidiTimeAxisView::step_add_program_change (uint8_t channel, uint8_t program)
1050 MidiTimeAxisView::step_edit_sustain (Evoral::MusicalTime beats)
1052 if (step_edit_region_view) {
1053 step_edit_region_view->step_sustain (beats);
1058 MidiTimeAxisView::move_step_edit_beat_pos (Evoral::MusicalTime beats)
1061 step_edit_beat_pos = min (step_edit_beat_pos + beats,
1062 step_edit_region_view->frames_to_beats (step_edit_region->length()));
1063 } else if (beats < 0.0) {
1064 if (beats < step_edit_beat_pos) {
1065 step_edit_beat_pos += beats; // its negative, remember
1067 step_edit_beat_pos = 0;
1070 step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1074 MidiTimeAxisView::step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, Evoral::MusicalTime beat_duration)
1076 /* do these things in case undo removed the step edit region
1078 if (!step_edit_region) {
1079 resync_step_edit_position ();
1080 prepare_step_edit_region ();
1081 reset_step_edit_beat_pos ();
1082 step_edit_region_view->show_step_edit_cursor (step_edit_beat_pos);
1083 step_edit_region_view->set_step_edit_cursor_width (step_editor->note_length());
1086 assert (step_edit_region);
1087 assert (step_edit_region_view);
1089 if (beat_duration == 0.0) {
1091 beat_duration = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
1098 MidiStreamView* msv = midi_view();
1100 /* make sure its visible on the vertical axis */
1102 if (pitch < msv->lowest_note() || pitch > msv->highest_note()) {
1103 msv->update_note_range (pitch);
1104 msv->set_note_range (MidiStreamView::ContentsRange);
1107 /* make sure its visible on the horizontal axis */
1109 nframes64_t fpos = step_edit_region->position() +
1110 step_edit_region_view->beats_to_frames (step_edit_beat_pos + beat_duration);
1112 if (fpos >= (_editor.leftmost_position() + _editor.current_page_frames())) {
1113 _editor.reset_x_origin (fpos - (_editor.current_page_frames()/4));
1116 step_edit_region_view->step_add_note (channel, pitch, velocity, step_edit_beat_pos, beat_duration);
1118 if (_step_edit_triplet_countdown > 0) {
1119 _step_edit_triplet_countdown--;
1121 if (_step_edit_triplet_countdown == 0) {
1122 _step_edit_triplet_countdown = 3;
1126 if (!_step_edit_within_chord) {
1127 step_edit_beat_pos += beat_duration;
1128 step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1130 step_edit_beat_pos += 1.0/Meter::ticks_per_beat; // tiny, but no longer overlapping
1131 _step_edit_chord_duration = max (_step_edit_chord_duration, beat_duration);
1138 MidiTimeAxisView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
1140 if (step_edit_region_view) {
1141 step_edit_region_view->set_step_edit_cursor_width (beats);
1146 MidiTimeAxisView::step_edit_within_triplet() const
1148 return _step_edit_triplet_countdown > 0;
1152 MidiTimeAxisView::step_edit_within_chord() const
1154 return _step_edit_within_chord;
1158 MidiTimeAxisView::step_edit_toggle_triplet ()
1160 if (_step_edit_triplet_countdown == 0) {
1161 _step_edit_within_chord = false;
1162 _step_edit_triplet_countdown = 3;
1164 _step_edit_triplet_countdown = 0;
1169 MidiTimeAxisView::step_edit_toggle_chord ()
1171 if (_step_edit_within_chord) {
1172 _step_edit_within_chord = false;
1173 step_edit_beat_pos += _step_edit_chord_duration;
1174 step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1176 _step_edit_triplet_countdown = 0;
1177 _step_edit_within_chord = true;
1182 MidiTimeAxisView::step_edit_rest (Evoral::MusicalTime beats)
1187 beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
1193 step_edit_beat_pos += beats;
1194 step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1199 MidiTimeAxisView::step_edit_beat_sync ()
1201 step_edit_beat_pos = ceil (step_edit_beat_pos);
1202 step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1206 MidiTimeAxisView::step_edit_bar_sync ()
1208 if (!_session || !step_edit_region_view || !step_edit_region) {
1212 framepos_t fpos = step_edit_region->position() +
1213 step_edit_region_view->beats_to_frames (step_edit_beat_pos);
1214 fpos = _session->tempo_map().round_to_bar (fpos, 1);
1215 step_edit_beat_pos = ceil (step_edit_region_view->frames_to_beats (fpos - step_edit_region->position()));
1216 step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1219 boost::shared_ptr<MidiRegion>
1220 MidiTimeAxisView::add_region (framepos_t pos)
1222 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1224 real_editor->begin_reversible_command (_("create region"));
1225 playlist()->clear_history ();
1227 real_editor->snap_to (pos, 0);
1228 const Meter& m = _session->tempo_map().meter_at(pos);
1229 const Tempo& t = _session->tempo_map().tempo_at(pos);
1230 double length = floor (m.frames_per_bar(t, _session->frame_rate()));
1232 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1233 view()->trackview().track()->name());
1236 plist.add (ARDOUR::Properties::start, 0);
1237 plist.add (ARDOUR::Properties::length, length);
1238 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1240 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1242 playlist()->add_region (region, pos);
1243 _session->add_command (new StatefulDiffCommand (playlist()));
1245 real_editor->commit_reversible_command();
1247 return boost::dynamic_pointer_cast<MidiRegion>(region);
1251 MidiTimeAxisView::add_note_selection (uint8_t note)
1253 if (!_editor.internal_editing()) {
1257 uint16_t chn_mask = _channel_selector.get_selected_channels();
1259 if (_view->num_selected_regionviews() == 0) {
1260 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1262 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1267 MidiTimeAxisView::extend_note_selection (uint8_t note)
1269 if (!_editor.internal_editing()) {
1273 uint16_t chn_mask = _channel_selector.get_selected_channels();
1275 if (_view->num_selected_regionviews() == 0) {
1276 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1278 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1283 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1285 if (!_editor.internal_editing()) {
1289 uint16_t chn_mask = _channel_selector.get_selected_channels();
1291 if (_view->num_selected_regionviews() == 0) {
1292 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1294 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1299 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1301 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1305 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1307 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1311 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1313 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1317 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1319 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1323 uint16_t selected_channels = _channel_selector.get_selected_channels();
1324 bool changed = false;
1328 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1330 for (uint32_t chn = 0; chn < 16; ++chn) {
1331 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1332 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1338 if ((selected_channels & (0x0001 << chn)) == 0) {
1339 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1340 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1342 changed = track->set_visibility (false) || changed;
1344 changed = track->set_visibility (true) || changed;
1351 /* TODO: Bender, PgmChange, Pressure */
1353 /* invalidate the controller menu, so that we rebuilt it next time */
1354 _controller_menu_map.clear ();
1355 delete controller_menu;
1356 controller_menu = 0;
1359 _route->gui_changed ("track_height", this);
1364 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1366 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1371 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1372 if (i != _controller_menu_map.end()) {
1376 i = _channel_command_menu_map.find (param);
1377 if (i != _channel_command_menu_map.end()) {