From 5db598fb0ad46b061f68b32bbb04adc503c8b12b Mon Sep 17 00:00:00 2001 From: Hoger Dehnhardt Date: Mon, 13 Nov 2023 21:56:57 +0100 Subject: [PATCH] c1: Add GUI for plugin mappings --- libs/surfaces/console1/c1_gui.cc | 166 +++++++++++++++++- libs/surfaces/console1/c1_gui.h | 68 ++++++- .../surfaces/console1/c1_plugin_operations.cc | 79 ++++++++- libs/surfaces/console1/console1.cc | 13 +- libs/surfaces/console1/console1.h | 46 +++-- 5 files changed, 340 insertions(+), 32 deletions(-) diff --git a/libs/surfaces/console1/c1_gui.cc b/libs/surfaces/console1/c1_gui.cc index ae37d3e0ac..c1977e3a34 100644 --- a/libs/surfaces/console1/c1_gui.cc +++ b/libs/surfaces/console1/c1_gui.cc @@ -22,6 +22,7 @@ #include #include +#include #include "pbd/file_utils.h" #include "pbd/i18n.h" #include "pbd/strsplit.h" @@ -32,6 +33,7 @@ #include "ardour/filesystem_paths.h" #include "ardour/parameter_descriptor.h" #include "console1.h" +#include "gtkmm2ext/action_model.h" #include "gtkmm2ext/bindings.h" #include "gtkmm2ext/gui_thread.h" #include "gtkmm2ext/utils.h" @@ -49,7 +51,7 @@ Console1::get_gui () const if (!gui) { const_cast (this)->build_gui (); } - static_cast (gui)->show_all (); + static_cast (gui)->show_all (); return gui; } @@ -57,7 +59,7 @@ void Console1::tear_down_gui () { if (gui) { - Gtk::Widget* w = static_cast (gui)->get_parent (); + Gtk::Widget* w = static_cast (gui)->get_parent (); if (w) { w->hide (); delete w; @@ -167,10 +169,44 @@ C1GUI::C1GUI (Console1& p) row++; hpacker.pack_start (table, true, true); + append_page (hpacker, _("Device Setup")); + hpacker.show_all(); - set_spacing (12); + // Create the page for plugin mappings + p.load_mappings (); - pack_start (hpacker, false, false); + VBox* plugconfig_packer = manage (new VBox); + HBox* plugselect_packer = manage (new HBox); + + l = manage (new Gtk::Label (_("Select Plugin"))); + plugselect_packer->pack_start (*l, false, false); + + plugconfig_packer->pack_start (*plugselect_packer, false, false); + + Glib::RefPtr plugin_store_model = ListStore::create (plugin_columns); + TreeModel::Row plugin_combo_row; + for( const auto &pm : c1.getPluginMappingMap() ){ + plugin_combo_row = *(plugin_store_model->append ()); + plugin_combo_row[plugin_columns.plugin_name] = pm.second.name; + plugin_combo_row[plugin_columns.plugin_id] = pm.first; + DEBUG_TRACE (DEBUG::Console1, string_compose ("Add Plugin: name %1 / %2\n", pm.second.name, pm.first)); + } + plugins_combo.pack_start (plugin_columns.plugin_name); + plugins_combo.signal_changed ().connect ( + sigc::bind (sigc::mem_fun (*this, &C1GUI::active_plugin_changed), &plugins_combo)); + plugins_combo.set_model (plugin_store_model); + + plugselect_packer->pack_start (plugins_combo, true, true); + plugin_mapping_scroller.property_shadow_type() = Gtk::SHADOW_NONE; + plugin_mapping_scroller.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC, Gtk::PolicyType::POLICY_AUTOMATIC); + + plugin_mapping_scroller.add (plugin_assignment_editor); + plugconfig_packer->pack_start (plugin_mapping_scroller, true, true, 20); + + build_plugin_assignment_editor (); + + append_page (*plugconfig_packer, _ ("Plugin Mappings")); + plugconfig_packer->show_all (); /* update the port connection combos */ @@ -186,7 +222,9 @@ C1GUI::C1GUI (Console1& p) _port_connections, invalidator (*this), std::bind (&C1GUI::connection_handler, this), gui_context ()); } -C1GUI::~C1GUI () {} +C1GUI::~C1GUI () { + write_plugin_assignment(); +} void C1GUI::set_swap_solo_mute () @@ -287,7 +325,7 @@ C1GUI::build_midi_port_list (vector const& ports, bool for_input) row = *store->append (); row[midi_port_columns.full_name] = string (); - row[midi_port_columns.short_name] = _ ("Disconnected"); + row[midi_port_columns.short_name] = _("Disconnected"); for (vector::const_iterator p = ports.begin (); p != ports.end (); ++p) { row = *store->append (); @@ -334,3 +372,119 @@ C1GUI::active_port_changed (Gtk::ComboBox* combo, bool for_input) } } } + +void +C1GUI::change_controller (const Glib::ustring &sPath, const TreeModel::iterator &iter) +{ + Gtk::TreePath path(sPath); + Gtk::TreeModel::iterator row = plugin_assignment_store->get_iter(path); + int index = *path.begin (); + if (row) { + + string controllerName = (*iter)[c1.plugin_controller_columns.controllerName]; + int controllerId = (*iter)[c1.plugin_controller_columns.controllerId]; + pc.parameters[index].controllerId = ArdourSurface::Console1::ControllerID (controllerId); + (*row).set_value (plugin_assignment_editor_columns.controllerName, controllerName); + DEBUG_TRACE (DEBUG::Console1, + string_compose ("Column Name: Controller, index %1, name %2 \n", index, controllerName)); + assignement_changed = true; + } +} + +void C1GUI::toggle_shift( const Glib::ustring& s){ + int index = atoi (s.c_str()); + Gtk::TreeModel::iterator row = plugin_assignment_store->get_iter (s); + if( row ) + { + bool value = !pc.parameters[index].shift; + pc.parameters[index].shift = value; + (*row).set_value (plugin_assignment_editor_columns.shift, value); + DEBUG_TRACE (DEBUG::Console1, string_compose ("Column Name: Shift, value %1\n", value)); + assignement_changed = true; + } +} + +CellRendererCombo* +C1GUI::make_action_renderer (Glib::RefPtr model, Gtk::TreeModelColumnBase column) +{ + CellRendererCombo* renderer = manage (new CellRendererCombo); + renderer->property_model() = model; + renderer->property_editable() = true; + renderer->property_text_column () = 0; + renderer->property_has_entry () = false; + renderer->signal_changed().connect (sigc::mem_fun(*this, &C1GUI::change_controller)); + + return renderer; +} + +void +C1GUI::build_plugin_assignment_editor () +{ + plugin_assignment_editor.append_column (_("Key"), plugin_assignment_editor_columns.index); + plugin_assignment_editor.append_column (_("Name"), plugin_assignment_editor_columns.name); + plugin_assignment_editor.append_column (_("Switch"), plugin_assignment_editor_columns.is_switch); + + TreeViewColumn* col; + CellRendererCombo* renderer; + + CellRendererToggle* boolRenderer = manage (new CellRendererToggle); + boolRenderer->set_active (); + boolRenderer->property_activatable() = true; + col = manage (new TreeViewColumn (_ ("Shift"), *boolRenderer)); + col->add_attribute (boolRenderer->property_active (), plugin_assignment_editor_columns.shift); + boolRenderer->signal_toggled().connect (sigc::mem_fun(*this, &C1GUI::toggle_shift)); + plugin_assignment_editor.append_column (*col); + + + renderer = make_action_renderer (c1.getPluginControllerModel(), plugin_assignment_editor_columns.controllerName); + col = manage (new TreeViewColumn (_("Control"), *renderer)); + col->add_attribute (renderer->property_text(), plugin_assignment_editor_columns.controllerName); + plugin_assignment_editor.append_column (*col); + + plugin_assignment_store = ListStore::create (plugin_assignment_editor_columns); + plugin_assignment_editor.set_model (plugin_assignment_store); +} + + +void +C1GUI::active_plugin_changed(Gtk::ComboBox* combo ){ + DEBUG_TRACE (DEBUG::Console1, "C1GUI active_plugin_changed\n"); + + write_plugin_assignment (); + + plugin_assignment_editor.set_model (Glib::RefPtr()); + plugin_assignment_store->clear (); + + TreeModel::iterator active = combo->get_active (); + TreeModel::Row plugin_assignment_row; + + string new_plugin_name = (*active)[plugin_columns.plugin_name]; + string new_plugin_id = (*active)[plugin_columns.plugin_id]; + DEBUG_TRACE (DEBUG::Console1, string_compose ("Plugin: selected %1 / %2\n", new_plugin_name, new_plugin_id)); + pc = c1.getPluginMappingMap ()[new_plugin_id]; + + for( auto &parm : pc.parameters ){ + plugin_assignment_row = *(plugin_assignment_store->append ()); + plugin_assignment_row[plugin_assignment_editor_columns.index] = parm.first; + plugin_assignment_row[plugin_assignment_editor_columns.name] = parm.second.name; + plugin_assignment_row[plugin_assignment_editor_columns.controllerName] = c1.findControllerNameById(parm.second.controllerId); + plugin_assignment_row[plugin_assignment_editor_columns.is_switch] = parm.second.is_switch; + plugin_assignment_row[plugin_assignment_editor_columns.shift] = parm.second.shift; + + DEBUG_TRACE (DEBUG::Console1, string_compose ("Parameter Name %1 \n", parm.second.name)); + DEBUG_TRACE (DEBUG::Console1, string_compose ("Parameter Index: %1 - index %2 \n", parm.first, parm.second.paramIndex)); + DEBUG_TRACE (DEBUG::Console1, string_compose ("ControllerId: %1 \n", parm.second.controllerId)); + DEBUG_TRACE (DEBUG::Console1, string_compose ("is switch? %1 \n", parm.second.is_switch)); + DEBUG_TRACE (DEBUG::Console1, string_compose ("is shift? %1 \n", parm.second.shift)); + } + plugin_assignment_editor.set_model (plugin_assignment_store); + +} + +void C1GUI::write_plugin_assignment(){ + DEBUG_TRACE (DEBUG::Console1, "write_plugin_assignment\n"); + if( !assignement_changed ) + return; + c1.write_plugin_mapping (pc); + assignement_changed = false; +} diff --git a/libs/surfaces/console1/c1_gui.h b/libs/surfaces/console1/c1_gui.h index e5408f82c1..45c66cd144 100644 --- a/libs/surfaces/console1/c1_gui.h +++ b/libs/surfaces/console1/c1_gui.h @@ -30,19 +30,32 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace Gtk { class CellRendererCombo; class ListStore; } +namespace ActionManager { + class ActionModel; +} + #include "ardour/mode.h" #include "console1.h" namespace ArdourSurface { -class C1GUI : public Gtk::VBox +class C1GUI : public Gtk::Notebook { public: C1GUI (Console1&); @@ -55,16 +68,24 @@ private: Gtk::Table table; Gtk::ComboBox input_combo; Gtk::ComboBox output_combo; + Gtk::ComboBox plugins_combo; + Gtk::ScrolledWindow plugin_mapping_scroller; Gtk::Image image; Gtk::CheckButton swap_solo_mute_cb; Gtk::CheckButton band_q_as_send_cb; Gtk::CheckButton create_plugin_stubs_btn; + Gtk::TreeView plugin_assignment_editor; + + Gtk::VBox plugin_packer; + + bool assignement_changed = false; + void update_port_combos (); PBD::ScopedConnection connection_change_connection; void connection_handler (); PBD::ScopedConnectionList _port_connections; - + struct MidiPortColumns : public Gtk::TreeModel::ColumnRecord { MidiPortColumns() { add (short_name); @@ -74,17 +95,54 @@ private: Gtk::TreeModelColumn full_name; }; + struct PluginColumns : public Gtk::TreeModel::ColumnRecord { + PluginColumns() { + add (plugin_name); + add (plugin_id); + } + Gtk::TreeModelColumn plugin_name; + Gtk::TreeModelColumn plugin_id; + }; + + struct PluginAssignamentEditorColumns : public Gtk::TreeModel::ColumnRecord { + PluginAssignamentEditorColumns() { + add (index); + add (name); + add (is_switch); + add (controllerName); + add (shift); + }; + Gtk::TreeModelColumn index; // parameter index + Gtk::TreeModelColumn name; // readable name of the parameter + Gtk::TreeModelColumn is_switch; + Gtk::TreeModelColumn controllerName; // enum Button::ID + Gtk::TreeModelColumn shift; + }; + MidiPortColumns midi_port_columns; + PluginColumns plugin_columns; + PluginAssignamentEditorColumns plugin_assignment_editor_columns; + Glib::RefPtr plugin_assignment_store; + bool ignore_active_change; Glib::RefPtr build_midi_port_list (std::vector const & ports, bool for_input); - void active_port_changed (Gtk::ComboBox*,bool for_input); + + ArdourSurface::Console1::PluginMapping pc; + Gtk::CellRendererCombo* make_action_renderer (Glib::RefPtr model, Gtk::TreeModelColumnBase column); + void build_plugin_assignment_editor (); + + void change_controller (const Glib::ustring &, const Gtk::TreeIter&); + void toggle_shift ( const Glib::ustring& ); + + void active_port_changed (Gtk::ComboBox*, bool for_input); void set_swap_solo_mute (); void set_band_q_as_send(); - void set_create_mapping_stubs(); + void set_create_mapping_stubs (); + void active_plugin_changed (Gtk::ComboBox* combo); + void write_plugin_assignment (); }; - } #endif /* __ardour_console1_gui_h__ */ diff --git a/libs/surfaces/console1/c1_plugin_operations.cc b/libs/surfaces/console1/c1_plugin_operations.cc index 6fd1d5ef48..a22252706b 100644 --- a/libs/surfaces/console1/c1_plugin_operations.cc +++ b/libs/surfaces/console1/c1_plugin_operations.cc @@ -57,6 +57,9 @@ Console1::ensure_config_dir () uint32_t Console1::load_mappings () { + if( mappings_loaded ) + return pluginMappingMap.size (); + uint32_t i = 0; if (!ensure_config_dir ()) return 1; @@ -89,7 +92,9 @@ Console1::load_mappings () ++i; } DEBUG_TRACE (DEBUG::Console1, string_compose ("Console1::load_mappings - found %1 mapping files\n", i)); + DEBUG_TRACE (DEBUG::Console1, string_compose ("Console1::load_mappings - loaded %1 mapping files\n", pluginMappingMap.size())); g_dir_close (gdir); + mappings_loaded = true; return i; } @@ -118,26 +123,32 @@ Console1::load_mapping (XMLNode* mapping_xml) const XMLNodeList& plist = (*i)->children (); XMLNodeConstIterator j; + PluginParameterMapping parmap; for (j = plist.begin (); j != plist.end (); ++j) { if ((*j)->name () == "name") { param_name = (*j)->child_content (); } else if ((*j)->name () == "mapping") { param_mapping = (*j)->child_content (); + (*j)->get_property ("shift", parmap.shift); } } + parmap.paramIndex = index; + parmap.name = param_name; + parmap.is_switch = (param_type == "switch"); if (!param_mapping.empty ()) { - PluginParameterMapping parmap; - parmap.paramIndex = index; - parmap.name = param_name; ControllerMap::const_iterator m = controllerMap.find (param_mapping); - if (m == controllerMap.end ()) - continue; - parmap.controllerId = m->second; - parmap.is_switch = (param_type == "switch"); - pm.parameters[index] = std::move (parmap); - pluginMappingMap[pm.id] = pm; + if (m != controllerMap.end ()) + { + parmap.controllerId = m->second; + } } + else{ + pm.configured = false; + parmap.controllerId = CONTROLLER_NONE; + } + pm.parameters[index] = std::move (parmap); } + pluginMappingMap[pm.id] = pm; return true; } @@ -182,6 +193,44 @@ Console1::create_mapping (const std::shared_ptr proc, const std::shar tree->write (); } +void +Console1::write_plugin_mapping (PluginMapping &mapping) +{ + DEBUG_TRACE (DEBUG::Console1, "write_plugin_mapping \n"); + XMLTree* tree = new XMLTree (); + XMLNode node = XMLNode ("c1plugin-mapping"); + node.set_property ("ID", mapping.id); + node.set_property ("NAME", mapping.name); + + for (const auto& plugin_param : mapping.parameters ) { + DEBUG_TRACE (DEBUG::Console1, string_compose ("Plugin parameter %1: %2\n",plugin_param.first ,plugin_param.second.name)); + XMLNode param = XMLNode ("param-mapping"); + param.set_property ("id", plugin_param.second.paramIndex); + XMLNode name = XMLNode ("name"); + XMLNode c = XMLNode ("c", plugin_param.second.name ); + name.add_child_copy (c); + XMLNode mapping = XMLNode ("mapping"); + mapping.set_property ("shift", plugin_param.second.shift); + XMLNode controller = XMLNode ("c", findControllerNameById(plugin_param.second.controllerId) ); + mapping.add_child_copy (controller); + param.add_child_copy (name); + param.add_child_copy (mapping); + node.add_child_copy (param); + } + + tree->set_root (&node); + + if (!ensure_config_dir ()) + return; + + std::string filename = Glib::build_filename ( + user_config_directory (), config_dir_name, string_compose ("%1.%2", mapping.id, "xml")); + + tree->set_filename (filename); + tree->write (); + load_mapping (&node); +} + bool Console1::select_plugin (const int32_t plugin_index) { @@ -444,4 +493,16 @@ Console1::spill_plugins (const int32_t plugin_index) return true; } +Glib::RefPtr Console1::getPluginControllerModel() +{ + plugin_controller_model = Gtk::ListStore::create (plugin_controller_columns); + Gtk::TreeModel::Row plugin_controller_combo_row; + for( const auto &controller : controllerMap ){ + plugin_controller_combo_row = *(plugin_controller_model->append ()); + plugin_controller_combo_row[plugin_controller_columns.controllerId] = controller.second; + plugin_controller_combo_row[plugin_controller_columns.controllerName] = X_(controller.first); + } + return plugin_controller_model; +} + } diff --git a/libs/surfaces/console1/console1.cc b/libs/surfaces/console1/console1.cc index 64d4b0d89e..c4ad5f57fc 100644 --- a/libs/surfaces/console1/console1.cc +++ b/libs/surfaces/console1/console1.cc @@ -181,7 +181,7 @@ Console1::begin_using_device () f0 7d 20 00 00 00 01 00 7f 49 6f 6c 73 00 f7 */ - load_mappings (); + load_mappings (); setup_controls (); /* Connection to the blink-timer */ @@ -532,7 +532,7 @@ Console1::notify_transport_state_changed () void Console1::stripable_selection_changed () { - DEBUG_TRACE (DEBUG::Console1, "stripable_selection_changed \n"); + DEBUG_TRACE (DEBUG::Console1, "stripable_selection_changed \n"); if (!_in_use) return; @@ -1261,3 +1261,12 @@ Console1::master_monitor_has_changed () DEBUG_TRACE (DEBUG::Console1, string_compose ("master_monitor_has_changed - monitor active %1\n", monitor_active)); create_strip_inventory (); } + +const std::string Console1::findControllerNameById (const ControllerID id){ + for( const auto &controller : controllerMap ){ + if( controller.second == id ){ + return controller.first; + } + } + return std::string(); +} \ No newline at end of file diff --git a/libs/surfaces/console1/console1.h b/libs/surfaces/console1/console1.h index 58cd9b3b94..e4cc90fbfc 100644 --- a/libs/surfaces/console1/console1.h +++ b/libs/surfaces/console1/console1.h @@ -23,6 +23,11 @@ #include #include +#include +#include + +#include + #define ABSTRACT_UI_EXPORTS #include "pbd/abstract_ui.h" @@ -109,6 +114,7 @@ public: std::string input_port_name () const override; std::string output_port_name () const override; + uint32_t load_mappings (); XMLNode& get_state () const override; int set_state (const XMLNode&, int version) override; @@ -293,12 +299,27 @@ public: { "TRACK_COPY", ControllerID::TRACK_COPY }, { "TRACK_GROUP", ControllerID::TRACK_GROUP } }; -private: + struct PluginControllerColumns : public Gtk::TreeModel::ColumnRecord { + PluginControllerColumns () { + add (controllerName); + add (controllerId); + } + Gtk::TreeModelColumn controllerName; + Gtk::TreeModelColumn controllerId; + }; + PluginControllerColumns plugin_controller_columns; + Glib::RefPtr plugin_controller_model; + const std::string findControllerNameById (const ControllerID id); + + private: std::string config_dir_name = "c1mappings"; /* GUI */ mutable C1GUI* gui; void build_gui (); + bool mappings_loaded = false; + bool controls_model_loaded = false; + /* Configuration */ const uint32_t bank_size = 20; @@ -630,16 +651,8 @@ private: using ParameterMap = std::map; - struct PluginMapping - { - std::string id; - std::string name; - ParameterMap parameters; - }; - /* plugin handling */ bool ensure_config_dir (); - uint32_t load_mappings (); bool load_mapping (XMLNode* fin); void create_mapping (const std::shared_ptr proc, const std::shared_ptr plugin); @@ -654,8 +667,21 @@ private: void eqBandQChangeMapping (bool mapValues); - using PluginMappingMap = std::map; + + public: + struct PluginMapping + { + std::string id; + std::string name; + bool configured; + ParameterMap parameters; + }; + using PluginMappingMap = std::map; PluginMappingMap pluginMappingMap; + + PluginMappingMap getPluginMappingMap () { return pluginMappingMap; } + Glib::RefPtr getPluginControllerModel(); + void write_plugin_mapping (PluginMapping &mapping); }; } #endif /* ardour_surface_console1_h */