diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index de2ed6a9b4..66ef38e28a 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -87,6 +87,7 @@ #include "midi_streamview.h" #include "midi_region_view.h" #include "midi_time_axis.h" +#include "midi_util.h" #include "patch_change_dialog.h" #include "patch_change_widget.h" #include "piano_roll_header.h" @@ -983,9 +984,9 @@ MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, /** Add a single menu item for a controller on one channel. */ void -MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items, - int ctl, - const std::string& name) +MidiTimeAxisView::add_single_channel_controller_item (Menu_Helpers::MenuList& ctl_items, + int ctl, + const std::string& name) { using namespace Menu_Helpers; @@ -1002,8 +1003,7 @@ MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl fully_qualified_param))); dynamic_cast (ctl_items.back().get_child())->set_use_markup (true); - std::shared_ptr track = automation_child ( - fully_qualified_param); + std::shared_ptr track = automation_child (fully_qualified_param); bool visible = false; if (track) { @@ -1090,137 +1090,12 @@ MidiTimeAxisView::build_controller_menu () } 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 - */ - - size_t total_ctrls = _route->instrument_info().master_controller_count (); - if (total_ctrls > 0) { - /* Controllers names available in midnam file, generate fancy menu */ - using namespace MIDI::Name; - - unsigned n_items = 0; - unsigned n_groups = 0; - - /* keep track of CC numbers that are added */ - uint16_t ctl_start = 1; - uint16_t ctl_end = 1; - - MasterDeviceNames::ControlNameLists const& ctllist (_route->instrument_info().master_device_names ()->controls ()); - - bool per_name_list = ctllist.size () > 1; - bool to_top_level = total_ctrls < 32 && !per_name_list; - - /* reverse lookup which "ChannelNameSet" has "UsesControlNameList " - * then check for which channels it is valid "AvailableForChannels" - */ - - for (MasterDeviceNames::ControlNameLists::const_iterator l = ctllist.begin(); l != ctllist.end(); ++l) { - - uint16_t channels = _route->instrument_info().channels_for_control_list (l->first); - bool multi_channel = 0 != (channels & (channels - 1)); - - std::shared_ptr name_list = l->second; - Menu* ctl_menu = NULL; - - for (ControlNameList::Controls::const_iterator c = name_list->controls().begin(); - c != name_list->controls().end();) { - - const uint16_t ctl = c->second->number(); - - /* Skip bank select controllers since they're handled specially */ - if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) { - - if (to_top_level) { - ctl_menu = controller_menu; - } else if (!ctl_menu) { - /* Create a new submenu */ - ctl_menu = manage (new Menu); - ctl_start = ctl; - } - - MenuList& ctl_items (ctl_menu->items()); - if (multi_channel) { - add_multi_channel_controller_item(ctl_items, channels, ctl, c->second->name()); - } else { - add_single_channel_controller_item(ctl_items, ctl, c->second->name()); - } - ctl_end = ctl; - } - - ++c; - - if (!ctl_menu || to_top_level) { - continue; - } - - if (++n_items == 32 || ctl < ctl_start || c == name_list->controls().end()) { - /* Submenu has 32 items or we're done, or a new name-list started: - * add it to controller menu and reset */ - items.push_back (MenuElem (string_compose ("%1 %2-%3", - (per_name_list ? l->first.c_str() : _("Controllers")), - ctl_start, ctl_end), *ctl_menu)); - ctl_menu = NULL; - n_items = 0; - ++n_groups; - } - } - } - } else { - /* No controllers names, generate generic numeric menu */ - - const uint16_t selected_channels = midi_track()->get_playback_channel_mask(); - - /* 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) { - break; - } - } - } - - for (int i = 0; i < 127; i += 32) { - Menu* ctl_menu = manage (new Menu); - MenuList& ctl_items (ctl_menu->items()); - - for (int ctl = i; ctl < i + 32; ++ctl) { - if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) { - /* Skip bank select controllers since they're handled specially */ - continue; - } - - if (chn_cnt > 1) { - add_multi_channel_controller_item( - ctl_items, selected_channels, ctl, string_compose(_("Controller %1"), ctl)); - } else { - add_single_channel_controller_item( - ctl_items, ctl, string_compose(_("Controller %1"), ctl)); - } - } - - /* Add submenu for this block of controllers to controller menu */ - switch (i) { - case 0: - case 32: - /* skip 0x00 and 0x20 (bank-select) */ - items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i + 1, i + 31), *ctl_menu)); - break; - default: - items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i + 31), *ctl_menu)); - break; - } - } - } + ::build_controller_menu (*controller_menu, _route->instrument_info(), midi_track()->get_playback_channel_mask(), + sigc::mem_fun (*this, &MidiTimeAxisView::add_single_channel_controller_item), + sigc::mem_fun (*this, &MidiTimeAxisView::add_multi_channel_controller_item)); } + Gtk::Menu* MidiTimeAxisView::build_note_mode_menu() { diff --git a/gtk2_ardour/midi_time_axis.h b/gtk2_ardour/midi_time_axis.h index 8d35e596fe..1e50777636 100644 --- a/gtk2_ardour/midi_time_axis.h +++ b/gtk2_ardour/midi_time_axis.h @@ -58,6 +58,7 @@ namespace ARDOUR { class Processor; class Location; class MidiPlaylist; + class InstrumentInfo; } namespace Evoral { @@ -209,4 +210,3 @@ private: void mouse_mode_changed (); PBD::ScopedConnection mouse_mode_connection; }; - diff --git a/gtk2_ardour/midi_util.cc b/gtk2_ardour/midi_util.cc new file mode 100644 index 0000000000..00436b551e --- /dev/null +++ b/gtk2_ardour/midi_util.cc @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2006-2015 David Robillard + * Copyright (C) 2008-2012 Hans Baier + * Copyright (C) 2008-2017 Paul Davis + * Copyright (C) 2009-2012 Carl Hetherington + * Copyright (C) 2013-2014 John Emmas + * Copyright (C) 2013-2016 Tim Mayberry + * Copyright (C) 2013-2019 Robin Gareus + * Copyright (C) 2014-2017 Ben Loftis + * Copyright (C) 2015-2017 Nick Mainsbridge + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include + +#include + +#include "ardour/event_type_map.h" +#include "ardour/instrument_info.h" +#include "ardour/midi_patch_manager.h" + +#include "midi++/events.h" + +#include "midi_util.h" + +#include "pbd/i18n.h" + +using namespace ARDOUR; +using namespace PBD; +using namespace Gtk; +using namespace std; + + +void +build_controller_menu (Gtk::Menu& menu, InstrumentInfo const & instrument_info, uint16_t channel_mask, + std::function add_single, + std::function add_multi) +{ + using namespace Menu_Helpers; + + /* 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 + */ + + MenuList& items (menu.items()); + + size_t total_ctrls = instrument_info.master_controller_count (); + if (total_ctrls > 0) { + /* Controllers names available in midnam file, generate fancy menu */ + using namespace MIDI::Name; + + unsigned n_items = 0; + unsigned n_groups = 0; + + /* keep track of CC numbers that are added */ + uint16_t ctl_start = 1; + uint16_t ctl_end = 1; + + MasterDeviceNames::ControlNameLists const& ctllist (instrument_info.master_device_names ()->controls ()); + + bool per_name_list = ctllist.size () > 1; + bool to_top_level = total_ctrls < 32 && !per_name_list; + + /* reverse lookup which "ChannelNameSet" has "UsesControlNameList " + * then check for which channels it is valid "AvailableForChannels" + */ + + for (MasterDeviceNames::ControlNameLists::const_iterator l = ctllist.begin(); l != ctllist.end(); ++l) { + + uint16_t channels = instrument_info.channels_for_control_list (l->first); + bool multi_channel = (0 != (channels & (channels - 1))); + + std::shared_ptr name_list = l->second; + Menu* ctl_menu = nullptr; + + for (ControlNameList::Controls::const_iterator c = name_list->controls().begin(); + c != name_list->controls().end();) { + + const uint16_t ctl = c->second->number(); + + /* Skip bank select controllers since they're handled specially */ + if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) { + + if (to_top_level) { + ctl_menu = &menu; + } else if (!ctl_menu) { + /* Create a new submenu */ + ctl_menu = manage (new Menu); + ctl_start = ctl; + } + + MenuList& ctl_items (ctl_menu->items()); + if (multi_channel) { + add_multi (ctl_items, channels, ctl, c->second->name()); + } else { + add_single (ctl_items, ctl, c->second->name()); + } + ctl_end = ctl; + } + + ++c; + + if (!ctl_menu || to_top_level) { + continue; + } + + if (++n_items == 32 || ctl < ctl_start || c == name_list->controls().end()) { + /* Submenu has 32 items or we're done, or a new name-list started: + * add it to controller menu and reset */ + items.push_back (MenuElem (string_compose ("%1 %2-%3", + (per_name_list ? l->first.c_str() : _("Controllers")), + ctl_start, ctl_end), *ctl_menu)); + ctl_menu = nullptr; + n_items = 0; + ++n_groups; + } + } + } + } else { + /* No controllers names, generate generic numeric menu */ + + bool multi_channel = (0 != (channel_mask & (channel_mask - 1))); + + /* count the number of selected channels because we will build a different menu + * structure if there is more than 1 selected. + */ + + for (int i = 0; i < 127; i += 32) { + Menu* ctl_menu = manage (new Menu); + MenuList& ctl_items (ctl_menu->items()); + + for (int ctl = i; ctl < i + 32; ++ctl) { + if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) { + /* Skip bank select controllers since they're handled specially */ + continue; + } + + if (multi_channel) { + add_multi (ctl_items, channel_mask, ctl, string_compose(_("Controller %1"), ctl)); + } else { + add_single (ctl_items, ctl, string_compose(_("Controller %1"), ctl)); + } + } + + /* Add submenu for this block of controllers to controller menu */ + switch (i) { + case 0: + case 32: + /* skip 0x00 and 0x20 (bank-select) */ + items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i + 1, i + 31), *ctl_menu)); + break; + default: + items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i + 31), *ctl_menu)); + break; + } + } + } +} diff --git a/gtk2_ardour/midi_util.h b/gtk2_ardour/midi_util.h index 14619a76ff..4f3c5a31a4 100644 --- a/gtk2_ardour/midi_util.h +++ b/gtk2_ardour/midi_util.h @@ -20,6 +20,18 @@ #pragma once #include +#include + +namespace Gtk { + class Menu; + namespace Menu_Helpers { + class MenuList; + } +} + +namespace ARDOUR { + class InstrumentInfo; +} inline static void clamp_to_0_127(uint8_t &val) { @@ -31,3 +43,7 @@ inline static void clamp_to_0_127(uint8_t &val) } +void +build_controller_menu (Gtk::Menu& menu, ARDOUR::InstrumentInfo const & instrument_info, uint16_t channel_mask, + std::function add_single, + std::function add_multi); diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 1951f8d1ec..0f947ee21a 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -167,6 +167,7 @@ gtk2_ardour_sources = [ 'midi_streamview.cc', 'midi_time_axis.cc', 'midi_tracer.cc', + 'midi_util.cc', 'midi_velocity_dialog.cc', 'midi_view.cc', 'midi_view_background.cc',