X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fmidi_time_axis.cc;h=82faf1890c089fa4fb5afaaf981a04a3ce683866;hb=fc77ae0738565770abde1a25f650a035cf082af0;hp=8ebe40c864ab0a7e54cd29cf66fb7e3a8a60cca6;hpb=65c2782e6ed4bd7c5ee2c39dc9dece66a0bb4578;p=ardour.git diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index 8ebe40c864..82faf1890c 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -38,31 +38,31 @@ #include "gtkmm2ext/bindable_button.h" #include "gtkmm2ext/utils.h" -#include "ardour/file_source.h" -#include "ardour/midi_playlist.h" -#include "ardour/midi_diskstream.h" +#include "ardour/event_type_map.h" #include "ardour/midi_patch_manager.h" +#include "ardour/midi_playlist.h" +#include "ardour/midi_region.h" #include "ardour/midi_source.h" -#include "ardour/processor.h" -#include "ardour/ladspa_plugin.h" -#include "ardour/location.h" +#include "ardour/midi_track.h" +#include "ardour/operations.h" #include "ardour/playlist.h" +#include "ardour/region.h" #include "ardour/region_factory.h" +#include "ardour/route.h" #include "ardour/session.h" -#include "ardour/session_playlist.h" -#include "ardour/tempo.h" -#include "ardour/utils.h" -#include "ardour/operations.h" +#include "ardour/session_object.h" +#include "ardour/source.h" +#include "ardour/track.h" +#include "ardour/types.h" #include "midi++/names.h" -#include "add_midi_cc_track_dialog.h" #include "ardour_ui.h" +#include "ardour_button.h" #include "automation_line.h" #include "automation_time_axis.h" #include "canvas-note-event.h" #include "canvas_impl.h" -#include "crossfade_view.h" #include "editor.h" #include "enums.h" #include "ghostregion.h" @@ -113,26 +113,48 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& can , _channel_color_mode_item(0) , _track_color_mode_item(0) , _step_edit_item (0) - , _midi_thru_item (0) , controller_menu (0) - , _step_editor (0) + , _step_editor (0) { } void MidiTimeAxisView::set_route (boost::shared_ptr rt) { + _route = rt; + + _view = new MidiStreamView (*this); + + if (is_track ()) { + _piano_roll_header = new PianoRollHeader(*midi_view()); + _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment); + _range_scroomer->DoubleClicked.connect ( + sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range), + MidiStreamView::ContentsRange, false)); + } + + /* This next call will result in our height being set up, so it must come after + the creation of the piano roll / range scroomer as their visibility is set up + when our height is. + */ RouteTimeAxisView::set_route (rt); + _view->apply_color (_color, StreamView::RegionColor); + subplugin_menu.set_name ("ArdourContextMenu"); - _view = new MidiStreamView (*this); + if (!gui_property ("note-range-min").empty ()) { + midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), + atoi (gui_property ("note-range-max").c_str()), + true); + } + midi_view()->NoteRangeChanged.connect ( + sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed)); + _view->ContentsHeightChanged.connect ( + sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed)); ignore_toggle = false; - mute_button->set_active (false); - solo_button->set_active (false); - if (is_midi_track()) { controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected"); _note_mode = midi_track()->note_mode(); @@ -144,32 +166,50 @@ MidiTimeAxisView::set_route (boost::shared_ptr rt) processors_changed (RouteProcessorChange ()); - _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context()); + _route->processors_changed.connect (*this, invalidator (*this), + boost::bind (&MidiTimeAxisView::processors_changed, this, _1), + gui_context()); if (is_track()) { - _piano_roll_header = new PianoRollHeader(*midi_view()); - - _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection)); - _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection)); - _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection)); - - _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment); + _piano_roll_header->SetNoteSelection.connect ( + sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection)); + _piano_roll_header->AddNoteSelection.connect ( + sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection)); + _piano_roll_header->ExtendNoteSelection.connect ( + sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection)); + _piano_roll_header->ToggleNoteSelection.connect ( + sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection)); /* Suspend updates of the StreamView during scroomer drags to speed things up */ - _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates)); - _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates)); - - controls_hbox.pack_start(*_range_scroomer); - controls_hbox.pack_start(*_piano_roll_header); + _range_scroomer->DragStarting.connect ( + sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates)); + _range_scroomer->DragFinishing.connect ( + sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates)); + + /* Put the scroomer and the keyboard in a VBox with a padding + label so that they can be reduced in height for stacked-view + tracks. + */ + VBox* v = manage (new VBox); + HBox* h = manage (new HBox); + h->pack_start (*_range_scroomer); + h->pack_start (*_piano_roll_header); + v->pack_start (*h, false, false); + v->pack_start (*manage (new Label ("")), true, true); + v->show (); + h->show (); + controls_hbox.pack_start(*v); controls_ebox.set_name ("MidiTrackControlsBaseUnselected"); controls_base_selected_name = "MidiTrackControlsBaseSelected"; controls_base_unselected_name = "MidiTrackControlsBaseUnselected"; - midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range)); + midi_view()->NoteRangeChanged.connect ( + sigc::mem_fun(*this, &MidiTimeAxisView::update_range)); /* ask for notifications of any new RegionViews */ - _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added)); + _view->RegionViewAdded.connect ( + sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added)); if (!_editor.have_idled()) { /* first idle will do what we need */ @@ -178,44 +218,59 @@ MidiTimeAxisView::set_route (boost::shared_ptr rt) } } - HBox* midi_controls_hbox = manage(new HBox()); MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance(); MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin(); for (; m != patch_manager.all_models().end(); ++m) { - _model_selector.append_text(m->c_str()); + _midnam_model_selector.append_text(m->c_str()); } - _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed)); + _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name"))); + model_changed(); + _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode"))); - _custom_device_mode_selector.signal_changed().connect( - sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed)); + _midnam_model_selector.signal_changed().connect( + sigc::mem_fun(*this, &MidiTimeAxisView::model_changed)); + _midnam_custom_device_mode_selector.signal_changed().connect( + sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed)); - // TODO: persist the choice - // this initializes the comboboxes and sends out the signal - _model_selector.set_active(0); + ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device")); + ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode")); - midi_controls_hbox->pack_start(_channel_selector, true, false); + _midi_controls_box.set_homogeneous(false); + _midi_controls_box.set_border_width (10); if (!patch_manager.all_models().empty()) { - _midi_controls_box.pack_start(_model_selector, true, false); - _midi_controls_box.pack_start(_custom_device_mode_selector, true, false); - } + _channel_selector.set_border_width(2); + _midi_controls_box.resize(3, 2); + _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1); - _midi_controls_box.pack_start(*midi_controls_hbox, true, true); + _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2); + + _midnam_model_selector.set_size_request(22, 30); + _midnam_model_selector.set_border_width(2); + _midi_controls_box.attach(_midnam_model_selector, 0, 1, 2, 3); + + _midnam_custom_device_mode_selector.set_size_request(10, 30); + _midnam_custom_device_mode_selector.set_border_width(2); + _midi_controls_box.attach(_midnam_custom_device_mode_selector, 0, 1, 3, 4); + } else { + _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1); + } controls_vbox.pack_start(_midi_controls_box, false, false); // restore channel selector settings - _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask()); + _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), + midi_track()->get_channel_mask()); _channel_selector.mode_changed.connect( sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode)); _channel_selector.mode_changed.connect( sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode)); - string prop = gui_property ("color-mode"); - if (!prop.empty()) { - _color_mode = ColorMode (string_2_enum(prop, _color_mode)); + const string color_mode = gui_property ("color-mode"); + if (!color_mode.empty()) { + _color_mode = ColorMode (string_2_enum(color_mode, _color_mode)); if (_color_mode == ChannelColors) { _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors); } @@ -223,13 +278,31 @@ MidiTimeAxisView::set_route (boost::shared_ptr rt) set_color_mode (_color_mode, true, false); - prop = gui_property ("note-mode"); - if (!prop.empty()) { - _note_mode = NoteMode (string_2_enum (prop, _note_mode)); + const string note_mode = gui_property ("note-mode"); + if (!note_mode.empty()) { + _note_mode = NoteMode (string_2_enum (note_mode, _note_mode)); if (_percussion_mode_item) { _percussion_mode_item->set_active (_note_mode == Percussive); } } + + /* Look for any GUI object state nodes that represent automation children + * that should exist, and create the children. + */ + + const list gui_ids = gui_object_state().all_ids (); + for (list::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) { + PBD::ID route_id; + bool has_parameter; + Evoral::Parameter parameter (0, 0, 0); + + bool const p = AutomationTimeAxisView::parse_state_id ( + *i, route_id, has_parameter, parameter); + if (p && route_id == _route->id () && has_parameter) { + const std::string& visible = gui_object_state().get_string (*i, X_("visible")); + create_automation_child (parameter, string_is_affirmative (visible)); + } + } } void @@ -249,53 +322,73 @@ MidiTimeAxisView::~MidiTimeAxisView () _range_scroomer = 0; delete controller_menu; - delete _step_editor; + delete _step_editor; } void MidiTimeAxisView::enter_internal_edit_mode () { - if (midi_view()) { - midi_view()->enter_internal_edit_mode (); - } + if (midi_view()) { + midi_view()->enter_internal_edit_mode (); + } } void MidiTimeAxisView::leave_internal_edit_mode () { - if (midi_view()) { - midi_view()->leave_internal_edit_mode (); - } + if (midi_view()) { + midi_view()->leave_internal_edit_mode (); + } } void MidiTimeAxisView::check_step_edit () { ensure_step_editor (); - _step_editor->check_step_edit (); + _step_editor->check_step_edit (); } void MidiTimeAxisView::model_changed() { + const Glib::ustring model = _midnam_model_selector.get_active_text(); + set_gui_property (X_("midnam-model-name"), model); + std::list device_modes = MIDI::Name::MidiPatchManager::instance() - .custom_device_mode_names_by_model(_model_selector.get_active_text()); + .custom_device_mode_names_by_model(model); - _custom_device_mode_selector.clear_items(); + _midnam_custom_device_mode_selector.clear_items(); - for (std::list::const_iterator i = device_modes.begin(); - i != device_modes.end(); ++i) { - cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl; - _custom_device_mode_selector.append_text(*i); + if (device_modes.size() < 2) { + _midnam_custom_device_mode_selector.hide(); + } else { + _midnam_custom_device_mode_selector.show(); + for (std::list::const_iterator i = device_modes.begin(); + i != device_modes.end(); ++i) { + _midnam_custom_device_mode_selector.append_text(*i); + } } - _custom_device_mode_selector.set_active(0); + _midnam_custom_device_mode_selector.set_active(0); + + _route->instrument_info().set_external_instrument ( + _midnam_model_selector.get_active_text(), + _midnam_custom_device_mode_selector.get_active_text()); + + // Rebuild controller menu + _controller_menu_map.clear (); + delete controller_menu; + controller_menu = 0; + build_automation_action_menu(false); } -void MidiTimeAxisView::custom_device_mode_changed() +void +MidiTimeAxisView::custom_device_mode_changed() { - _midi_patch_settings_changed.emit(_model_selector.get_active_text(), - _custom_device_mode_selector.get_active_text()); + const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text(); + set_gui_property (X_("midnam-custom-device-mode"), mode); + _route->instrument_info().set_external_instrument ( + _midnam_model_selector.get_active_text(), mode); } MidiStreamView* @@ -307,15 +400,13 @@ MidiTimeAxisView::midi_view() void MidiTimeAxisView::set_height (uint32_t h) { - RouteTimeAxisView::set_height (h); - - if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) { - _midi_controls_box.show_all (); + if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) { + _midi_controls_box.show (); } else { _midi_controls_box.hide(); } - if (height >= KEYBOARD_MIN_HEIGHT) { + if (h >= KEYBOARD_MIN_HEIGHT) { if (is_track() && _range_scroomer) { _range_scroomer->show(); } @@ -330,6 +421,13 @@ MidiTimeAxisView::set_height (uint32_t h) _piano_roll_header->hide(); } } + + /* We need to do this after changing visibility of our stuff, as it will + eventually trigger a call to Editor::reset_controls_layout_width(), + which needs to know if we have just shown or hidden a scroomer / + piano roll. + */ + RouteTimeAxisView::set_height (h); } void @@ -344,36 +442,22 @@ MidiTimeAxisView::append_extra_display_menu_items () MenuList& range_items = range_menu->items(); range_menu->set_name ("ArdourContextMenu"); - range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind ( - sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range), - MidiStreamView::FullRange))); + range_items.push_back ( + MenuElem (_("Show Full Range"), + sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range), + MidiStreamView::FullRange, true))); - range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind ( - sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range), - MidiStreamView::ContentsRange))); + range_items.push_back ( + MenuElem (_("Fit Contents"), + sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range), + MidiStreamView::ContentsRange, true))); items.push_back (MenuElem (_("Note Range"), *range_menu)); items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu())); - items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru))); - _midi_thru_item = dynamic_cast(&items.back()); - items.push_back (SeparatorElem ()); } -void -MidiTimeAxisView::toggle_midi_thru () -{ - if (!_midi_thru_item) { - return; - } - - bool view_yn = _midi_thru_item->get_active(); - if (view_yn != midi_track()->midi_thru()) { - midi_track()->set_midi_thru (view_yn); - } -} - void MidiTimeAxisView::build_automation_action_menu (bool for_selection) { @@ -401,38 +485,45 @@ MidiTimeAxisView::build_automation_action_menu (bool for_selection) automation_items.push_back (SeparatorElem()); - /* these 2 MIDI "command" types are semantically more like automation than note data, - but they are not MIDI controllers. We give them special status in this menu, since - they will not show up in the controller list and anyone who actually knows - something about MIDI (!) would not expect to find them there. + /* these 2 MIDI "command" types are semantically more like automation + than note data, but they are not MIDI controllers. We give them + special status in this menu, since they will not show up in the + controller list and anyone who actually knows something about MIDI + (!) would not expect to find them there. */ - add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0); - automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1); - add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0); - automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1); - - /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu - since it might need to be updated after a channel mode change or other change. Also detach it - first in case it has been used anywhere else. + add_channel_command_menu_item ( + automation_items, _("Bender"), MidiPitchBenderAutomation, 0); + automation_items.back().set_sensitive ( + !for_selection || _editor.get_selection().tracks.size() == 1); + add_channel_command_menu_item ( + automation_items, _("Pressure"), MidiChannelPressureAutomation, 0); + automation_items.back().set_sensitive ( + !for_selection || _editor.get_selection().tracks.size() == 1); + + /* now all MIDI controllers. Always offer the possibility that we will + rebuild the controllers menu since it might need to be updated after + a channel mode change or other change. Also detach it first in case + it has been used anywhere else. */ build_controller_menu (); automation_items.push_back (SeparatorElem()); automation_items.push_back (MenuElem (_("Controllers"), *controller_menu)); - automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1); + automation_items.back().set_sensitive ( + !for_selection || _editor.get_selection().tracks.size() == 1); } else { - automation_items.push_back (MenuElem (string_compose ("%1", _("No MIDI Channels selected")))); + automation_items.push_back ( + MenuElem (string_compose ("%1", _("No MIDI Channels selected")))); dynamic_cast (automation_items.back().get_child())->set_use_markup (true); } - } void MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param) { - uint16_t selected_channels = _channel_selector.get_selected_channels(); + const uint16_t selected_channels = _channel_selector.get_selected_channels(); for (uint8_t chn = 0; chn < 16; chn++) { if (selected_channels & (0x0001 << chn)) { @@ -448,14 +539,18 @@ MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Paramet } void -MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd) +MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, + const string& label, + AutomationType auto_type, + uint8_t cmd) { using namespace Menu_Helpers; - /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected. + /* count the number of selected channels because we will build a different menu + structure if there is more than 1 selected. */ - uint16_t selected_channels = _channel_selector.get_selected_channels(); + const uint16_t selected_channels = _channel_selector.get_selected_channels(); int chn_cnt = 0; for (uint8_t chn = 0; chn < 16; chn++) { @@ -476,12 +571,14 @@ MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, /* add a couple of items to hide/show all of them */ - chn_items.push_back (MenuElem (_("Hide all channels"), - sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), - false, param_without_channel))); - chn_items.push_back (MenuElem (_("Show all channels"), - sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), - true, param_without_channel))); + chn_items.push_back ( + MenuElem (_("Hide all channels"), + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), + false, param_without_channel))); + chn_items.push_back ( + MenuElem (_("Show all channels"), + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), + true, param_without_channel))); for (uint8_t chn = 0; chn < 16; chn++) { if (selected_channels & (0x0001 << chn)) { @@ -489,9 +586,10 @@ MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, /* for each selected channel, add a menu item for this controller */ Evoral::Parameter fully_qualified_param (auto_type, chn, cmd); - chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1), - sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track), - fully_qualified_param))); + chn_items.push_back ( + CheckMenuElem (string_compose (_("Channel %1"), chn+1), + sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track), + fully_qualified_param))); boost::shared_ptr track = automation_child (fully_qualified_param); bool visible = false; @@ -520,9 +618,10 @@ MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, if (selected_channels & (0x0001 << chn)) { Evoral::Parameter fully_qualified_param (auto_type, chn, cmd); - items.push_back (CheckMenuElem (label, - sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track), - fully_qualified_param))); + items.push_back ( + CheckMenuElem (label, + sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track), + fully_qualified_param))); boost::shared_ptr track = automation_child (fully_qualified_param); bool visible = false; @@ -550,24 +649,25 @@ MidiTimeAxisView::build_controller_menu () using namespace Menu_Helpers; if (controller_menu) { - /* it exists and has not been invalidated by a channel mode change, so just return it */ + /* it exists and has not been invalidated by a channel mode change */ return; } controller_menu = new Menu; // explicitly managed by us MenuList& items (controller_menu->items()); - /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu - for each controller+channel combination covering the currently selected channels for this track + /* create several "top level" menu items for sets of controllers (16 at a + time), and populate each one with a submenu for each controller+channel + combination covering the currently selected channels for this track */ - uint16_t selected_channels = _channel_selector.get_selected_channels(); + const uint16_t selected_channels = _channel_selector.get_selected_channels(); - /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected. - */ + /* count the number of selected channels because we will build a different menu + structure if there is more than 1 selected. + */ int chn_cnt = 0; - for (uint8_t chn = 0; chn < 16; chn++) { if (selected_channels & (0x0001 << chn)) { if (++chn_cnt > 1) { @@ -576,6 +676,43 @@ MidiTimeAxisView::build_controller_menu () } } + using namespace MIDI::Name; + const Glib::ustring model = _midnam_model_selector.get_active_text(); + boost::shared_ptr midnam = MidiPatchManager::instance() + .document_by_model(model); + boost::shared_ptr device_names; + if (midnam && !midnam->master_device_names_by_model().empty()) { + device_names = boost::shared_ptr( + midnam->master_device_names_by_model().begin()->second); + } + + if (device_names && !device_names->controls().empty()) { + /* Controllers names available from the midnam file, + generate a custom controller menu */ + for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin(); + l != device_names->controls().end(); ++l) { + boost::shared_ptr name_list = *l; + + int chn = 0; // TODO + Menu* group_menu = manage(new Menu()); + MenuList& group_items(group_menu->items()); + + for (ControlNameList::Controls::const_iterator c = (*l)->controls().begin(); + c != (*l)->controls().end(); ++c) { + Evoral::Parameter fully_qualified_param(MidiCCAutomation, chn, atoi((*c)->number().c_str())); + group_items.push_back( + CheckMenuElem(string_compose("%1: %2 [%3]", + (*c)->number(), (*c)->name(), int(chn)), + sigc::bind( + sigc::mem_fun(*this, &RouteTimeAxisView::toggle_automation_track), + fully_qualified_param))); + dynamic_cast (group_items.back().get_child())->set_use_markup (true); + } + items.push_back(MenuElem(name_list->name(), *group_menu)); + } + return; + } + /* loop over all 127 MIDI controllers, in groups of 16; except don't offer bank select controllers, as they are handled by the `patch' code */ @@ -584,7 +721,6 @@ MidiTimeAxisView::build_controller_menu () Menu* ctl_menu = manage (new Menu); MenuList& ctl_items (ctl_menu->items()); - /* for each controller, consider whether to create a submenu or a single item */ for (int ctl = i; ctl < i+16; ++ctl) { @@ -603,12 +739,14 @@ MidiTimeAxisView::build_controller_menu () /* add a couple of items to hide/show this controller on all channels */ Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl); - chn_items.push_back (MenuElem (_("Hide all channels"), - sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), - false, param_without_channel))); - chn_items.push_back (MenuElem (_("Show all channels"), - sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), - true, param_without_channel))); + chn_items.push_back ( + MenuElem (_("Hide all channels"), + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), + false, param_without_channel))); + chn_items.push_back ( + MenuElem (_("Show all channels"), + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), + true, param_without_channel))); for (uint8_t chn = 0; chn < 16; chn++) { if (selected_channels & (0x0001 << chn)) { @@ -616,11 +754,13 @@ MidiTimeAxisView::build_controller_menu () /* for each selected channel, add a menu item for this controller */ Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl); - chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1), - sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track), - fully_qualified_param))); + chn_items.push_back ( + CheckMenuElem (string_compose (_("Channel %1"), chn+1), + sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track), + fully_qualified_param))); - boost::shared_ptr track = automation_child (fully_qualified_param); + boost::shared_ptr track = automation_child ( + fully_qualified_param); bool visible = false; if (track) { @@ -636,12 +776,13 @@ MidiTimeAxisView::build_controller_menu () } /* add the per-channel menu to the list of controllers, with the name of the controller */ - ctl_items.push_back (MenuElem (string_compose ("%1: %2", ctl, midi_name (ctl)), *chn_menu)); + ctl_items.push_back (MenuElem (string_compose ("%1: %2", ctl, midi_name (ctl)), + *chn_menu)); dynamic_cast (ctl_items.back().get_child())->set_use_markup (true); } else { - /* just one channel - create a single menu item for this ctl+channel combination*/ + /* just one channel - create a single menu item for this ctl+channel combination */ for (uint8_t chn = 0; chn < 16; chn++) { if (selected_channels & (0x0001 << chn)) { @@ -650,15 +791,15 @@ MidiTimeAxisView::build_controller_menu () ctl_items.push_back ( CheckMenuElem ( string_compose ("%1: %2 [%3]", ctl, midi_name (ctl), int (chn)), - sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track), - fully_qualified_param) - ) - ); + sigc::bind ( + sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track), + fully_qualified_param))); dynamic_cast (ctl_items.back().get_child())->set_use_markup (true); - boost::shared_ptr track = automation_child (fully_qualified_param); - bool visible = false; + boost::shared_ptr track = automation_child ( + fully_qualified_param); + bool visible = false; if (track) { if (track->marked_for_display()) { visible = true; @@ -692,13 +833,17 @@ MidiTimeAxisView::build_note_mode_menu() mode_menu->set_name ("ArdourContextMenu"); RadioMenuItem::Group mode_group; - items.push_back (RadioMenuElem (mode_group, _("Sustained"), - sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained))); + items.push_back ( + RadioMenuElem (mode_group,_("Sustained"), + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), + Sustained, true))); _note_mode_item = dynamic_cast(&items.back()); _note_mode_item->set_active(_note_mode == Sustained); - items.push_back (RadioMenuElem (mode_group, _("Percussive"), - sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive))); + items.push_back ( + RadioMenuElem (mode_group, _("Percussive"), + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), + Percussive, true))); _percussion_mode_item = dynamic_cast(&items.back()); _percussion_mode_item->set_active(_note_mode == Percussive); @@ -715,21 +860,24 @@ MidiTimeAxisView::build_color_mode_menu() mode_menu->set_name ("ArdourContextMenu"); RadioMenuItem::Group mode_group; - items.push_back (RadioMenuElem (mode_group, _("Meter Colors"), - sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), - MeterColors, false, true))); + items.push_back ( + RadioMenuElem (mode_group, _("Meter Colors"), + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), + MeterColors, false, true, true))); _meter_color_mode_item = dynamic_cast(&items.back()); _meter_color_mode_item->set_active(_color_mode == MeterColors); - items.push_back (RadioMenuElem (mode_group, _("Channel Colors"), - sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), - ChannelColors, false, true))); + items.push_back ( + RadioMenuElem (mode_group, _("Channel Colors"), + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), + ChannelColors, false, true, true))); _channel_color_mode_item = dynamic_cast(&items.back()); _channel_color_mode_item->set_active(_color_mode == ChannelColors); - items.push_back (RadioMenuElem (mode_group, _("Track Color"), - sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), - TrackColor, false, true))); + items.push_back ( + RadioMenuElem (mode_group, _("Track Color"), + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), + TrackColor, false, true, true))); _channel_color_mode_item = dynamic_cast(&items.back()); _channel_color_mode_item->set_active(_color_mode == TrackColor); @@ -737,50 +885,65 @@ MidiTimeAxisView::build_color_mode_menu() } void -MidiTimeAxisView::set_note_mode(NoteMode mode) +MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection) { - if (_note_mode != mode || midi_track()->note_mode() != mode) { - _note_mode = mode; - midi_track()->set_note_mode(mode); - set_gui_property ("note-mode", enum_2_string(_note_mode)); - _view->redisplay_track(); + if (apply_to_selection) { + _editor.get_selection().tracks.foreach_midi_time_axis ( + boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false)); + } else { + if (_note_mode != mode || midi_track()->note_mode() != mode) { + _note_mode = mode; + midi_track()->set_note_mode(mode); + set_gui_property ("note-mode", enum_2_string(_note_mode)); + _view->redisplay_track(); + } } } void -MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay) +MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection) { - if (_color_mode == mode && !force) { - return; - } - - if (mode == ChannelColors) { - _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors); + if (apply_to_selection) { + _editor.get_selection().tracks.foreach_midi_time_axis ( + boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false)); } else { - _channel_selector.set_default_channel_color(); - } - - _color_mode = mode; - set_gui_property ("color-mode", enum_2_string(_color_mode)); - if (redisplay) { - _view->redisplay_track(); + if (_color_mode == mode && !force) { + return; + } + + if (mode == ChannelColors) { + _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors); + } else { + _channel_selector.set_default_channel_color(); + } + + _color_mode = mode; + set_gui_property ("color-mode", enum_2_string(_color_mode)); + if (redisplay) { + _view->redisplay_track(); + } } } void -MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range) +MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection) { - if (!_ignore_signals) - midi_view()->set_note_range(range); + if (apply_to_selection) { + _editor.get_selection().tracks.foreach_midi_time_axis ( + boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false)); + } else { + if (!_ignore_signals) { + midi_view()->set_note_range(range); + } + } } - void MidiTimeAxisView::update_range() { MidiGhostRegion* mgr; - for(list::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { + for (list::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { if ((mgr = dynamic_cast(*i)) != 0) { mgr->update_range(); } @@ -791,7 +954,8 @@ void MidiTimeAxisView::show_all_automation (bool apply_to_selection) { if (apply_to_selection) { - _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false)); + _editor.get_selection().tracks.foreach_midi_time_axis ( + boost::bind (&MidiTimeAxisView::show_all_automation, _1, false)); } else { if (midi_track()) { const set params = midi_track()->midi_playlist()->contained_automation(); @@ -809,7 +973,8 @@ void MidiTimeAxisView::show_existing_automation (bool apply_to_selection) { if (apply_to_selection) { - _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false)); + _editor.get_selection().tracks.foreach_midi_time_axis ( + boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false)); } else { if (midi_track()) { const set params = midi_track()->midi_playlist()->contained_automation(); @@ -829,7 +994,6 @@ void MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show) { if (param.type() == NullAutomation) { - cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl; return; } @@ -842,8 +1006,6 @@ MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool * since it will have been set visible by default. */ - cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl; - if (existing->second->set_marked_for_display (show) && !no_redraw) { request_redraw (); } @@ -875,27 +1037,28 @@ MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool */ track.reset (new AutomationTimeAxisView ( - _session, - _route, - boost::shared_ptr (), - boost::shared_ptr (), - param, - _editor, - *this, - true, - parent_canvas, - _route->describe_parameter(param) - )); + _session, + _route, + boost::shared_ptr (), + boost::shared_ptr (), + param, + _editor, + *this, + true, + parent_canvas, + _route->describe_parameter(param))); if (_view) { - _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost)); + _view->foreach_regionview ( + sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost)); } add_automation_child (param, track, show); break; default: - error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg; + error << "MidiTimeAxisView: unknown automation child " + << EventTypeMap::instance().to_symbol(param) << endmsg; } } @@ -915,9 +1078,6 @@ MidiTimeAxisView::route_active_changed () controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected"; } } else { - - throw; // wha? - if (_route->active()) { controls_ebox.set_name ("BusControlsBaseUnselected"); controls_base_selected_name = "BusControlsBaseSelected"; @@ -930,7 +1090,25 @@ MidiTimeAxisView::route_active_changed () } } +void +MidiTimeAxisView::set_note_selection (uint8_t note) +{ + if (!_editor.internal_editing()) { + return; + } + + uint16_t chn_mask = _channel_selector.get_selected_channels(); + if (_view->num_selected_regionviews() == 0) { + _view->foreach_regionview ( + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), + note, chn_mask)); + } else { + _view->foreach_selected_regionview ( + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), + note, chn_mask)); + } +} void MidiTimeAxisView::add_note_selection (uint8_t note) @@ -939,12 +1117,16 @@ MidiTimeAxisView::add_note_selection (uint8_t note) return; } - uint16_t chn_mask = _channel_selector.get_selected_channels(); + const uint16_t chn_mask = _channel_selector.get_selected_channels(); if (_view->num_selected_regionviews() == 0) { - _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask)); + _view->foreach_regionview ( + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), + note, chn_mask)); } else { - _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask)); + _view->foreach_selected_regionview ( + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), + note, chn_mask)); } } @@ -955,12 +1137,16 @@ MidiTimeAxisView::extend_note_selection (uint8_t note) return; } - uint16_t chn_mask = _channel_selector.get_selected_channels(); + const uint16_t chn_mask = _channel_selector.get_selected_channels(); if (_view->num_selected_regionviews() == 0) { - _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask)); + _view->foreach_regionview ( + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), + note, chn_mask)); } else { - _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask)); + _view->foreach_selected_regionview ( + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), + note, chn_mask)); } } @@ -971,21 +1157,31 @@ MidiTimeAxisView::toggle_note_selection (uint8_t note) return; } - uint16_t chn_mask = _channel_selector.get_selected_channels(); + const uint16_t chn_mask = _channel_selector.get_selected_channels(); if (_view->num_selected_regionviews() == 0) { - _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask)); + _view->foreach_regionview ( + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), + note, chn_mask)); } else { - _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask)); + _view->foreach_selected_regionview ( + sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), + note, chn_mask)); } } void -MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask) +MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask) { dynamic_cast(rv)->select_matching_notes (note, chn_mask, false, false); } +void +MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask) +{ + dynamic_cast(rv)->select_matching_notes (note, chn_mask, true, false); +} + void MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask) { @@ -1005,7 +1201,7 @@ MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t) the right ones. */ - uint16_t selected_channels = _channel_selector.get_selected_channels(); + const uint16_t selected_channels = _channel_selector.get_selected_channels(); bool changed = false; no_redraw = true; @@ -1023,7 +1219,7 @@ MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t) if ((selected_channels & (0x0001 << chn)) == 0) { /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden() which will cause a redraw. We don't want one per channel, so block that with no_redraw. - */ + */ changed = track->set_marked_for_display (false) || changed; } else { changed = track->set_marked_for_display (true) || changed; @@ -1072,12 +1268,12 @@ MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit) Editor* real_editor = dynamic_cast (&_editor); real_editor->begin_reversible_command (Operations::create_region); - playlist()->clear_changes (); + playlist()->clear_changes (); real_editor->snap_to (pos, 0); - boost::shared_ptr src = _session->create_midi_source_for_session (view()->trackview().track().get(), - view()->trackview().track()->name()); + boost::shared_ptr src = _session->create_midi_source_for_session ( + view()->trackview().track().get(), view()->trackview().track()->name()); PropertyList plist; plist.add (ARDOUR::Properties::start, 0); @@ -1108,15 +1304,15 @@ void MidiTimeAxisView::start_step_editing () { ensure_step_editor (); - _step_editor->start_step_editing (); + _step_editor->start_step_editing (); } void MidiTimeAxisView::stop_step_editing () { - if (_step_editor) { - _step_editor->stop_step_editing (); - } + if (_step_editor) { + _step_editor->stop_step_editing (); + } } @@ -1147,3 +1343,16 @@ MidiTimeAxisView::get_channel_for_add () const return channel; } + +void +MidiTimeAxisView::note_range_changed () +{ + set_gui_property ("note-range-min", (int) midi_view()->lowest_note ()); + set_gui_property ("note-range-max", (int) midi_view()->highest_note ()); +} + +void +MidiTimeAxisView::contents_height_changed () +{ + _range_scroomer->set_size_request (-1, _view->child_height ()); +}