From 835598c8020f913079b30bc29a134e9eab9c9db5 Mon Sep 17 00:00:00 2001 From: Hoger Dehnhardt Date: Fri, 12 May 2023 23:17:09 +0200 Subject: [PATCH] Console1: Add plugin interface --- libs/surfaces/console1/c1_control.h | 6 +- libs/surfaces/console1/c1_operations.cc | 30 +++ .../surfaces/console1/c1_plugin_operations.cc | 240 ++++++++++++++++++ libs/surfaces/console1/console1.cc | 21 +- libs/surfaces/console1/console1.h | 113 ++++++++- libs/surfaces/console1/wscript | 1 + 6 files changed, 405 insertions(+), 6 deletions(-) create mode 100644 libs/surfaces/console1/c1_plugin_operations.cc diff --git a/libs/surfaces/console1/c1_control.h b/libs/surfaces/console1/c1_control.h index 4c01cb4705..e3ec5cb022 100644 --- a/libs/surfaces/console1/c1_control.h +++ b/libs/surfaces/console1/c1_control.h @@ -45,10 +45,13 @@ class ControllerButton : public Controller ControllerButton (Console1& console1, ControllerID id, boost::function action, - boost::function shift_action = 0) + boost::function shift_action = 0, + boost::function plugin_action = 0 + ) : Controller (console1, id) , action (action) , shift_action (shift_action) + , plugin_action (plugin_action) { console1.buttons.insert (std::make_pair (id, *this)); } @@ -78,6 +81,7 @@ class ControllerButton : public Controller } boost::function action; boost::function shift_action; + boost::function plugin_action; }; class MultiStateButton : public Controller diff --git a/libs/surfaces/console1/c1_operations.cc b/libs/surfaces/console1/c1_operations.cc index 6925e8d2f0..d65a45cc65 100644 --- a/libs/surfaces/console1/c1_operations.cc +++ b/libs/surfaces/console1/c1_operations.cc @@ -146,6 +146,14 @@ Console1::shift (const uint32_t val) ShiftChange (val); } +void +Console1::plugin_state (const uint32_t) +{ + DEBUG_TRACE (DEBUG::Console1, "plugin_state()\n"); + in_plugin_state = !in_plugin_state; + PluginStateChange (in_plugin_state); +} + void Console1::solo (const uint32_t) { @@ -657,6 +665,28 @@ Console1::map_shift (bool shift) } } +void +Console1::map_plugin_state (bool plugin_state) +{ + DEBUG_TRACE (DEBUG::Console1, "map_plugin_state()\n"); + try { + ControllerButton& controllerButton = static_cast (get_button (TRACK_GROUP)); + controllerButton.set_led_state (in_plugin_state); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } + if (!plugin_state) { + for (uint32_t i = 0; i < bank_size; ++i) { + stop_blinking (ControllerID (FOCUS1 + i)); + } + map_stripable_state (); + } else { + // I don't plan shift functionality with plugins... + shift (0); + // map all plugin related operations + } +} + void Console1::map_solo () { diff --git a/libs/surfaces/console1/c1_plugin_operations.cc b/libs/surfaces/console1/c1_plugin_operations.cc new file mode 100644 index 0000000000..fdbd03dc3f --- /dev/null +++ b/libs/surfaces/console1/c1_plugin_operations.cc @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2023 Holger Dehnhardt + * + * 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 + +#include "glib-2.0/gio/gio.h" +#include "glib-2.0/glib/gstdio.h" +#include "glibmm-2.4/glibmm/main.h" +#include "glibmm-2.4/glibmm/miscutils.h" +#include "pbd/debug.h" + +#include "ardour/filesystem_paths.h" +#include "ardour/plugin_insert.h" +#include "ardour/processor.h" +#include "ardour/route.h" +#include "c1_control.h" +#include "console1.h" + +using namespace ARDOUR; +using namespace ArdourSurface; +using namespace PBD; +using namespace Glib; +using namespace std; + +namespace ArdourSurface { + +uint32_t +Console1::load_mappings () +{ + uint32_t i = 0; + std::string path = Glib::build_filename (user_config_directory (), "c1mappings"); + GError* error; + GFile* dir = g_file_new_for_path (path.c_str ()); + if (!g_file_test (path.c_str (), G_FILE_TEST_IS_DIR)) { + g_file_make_directory (dir, NULL, &error); + } + const gchar* fileName; + GDir* gdir = g_dir_open (path.c_str (), 0, NULL); + if (gdir == NULL) + return 0; + while ((fileName = g_dir_read_name (gdir)) != NULL) { + // if (!g_str_has_suffix (name, ".remmina")) + // continue; + DEBUG_TRACE (DEBUG::Console1, + string_compose ("Console1::load_mappings - found mapping file: '%1'\n", fileName)); + + std::string filePath = Glib::build_filename (path, fileName); + FILE* fin = g_fopen (filePath.c_str (), "r"); + if (fin) { + DEBUG_TRACE (DEBUG::Console1, + string_compose ("Console1::load_mappings - opened mapping file: '%1'\n", filePath)); + load_mapping (fin); + fclose (fin); + } + + ++i; + } + DEBUG_TRACE (DEBUG::Console1, string_compose ("Console1::load_mappings - found %1 mapping files\n", i)); + g_dir_close (gdir); + return i; +} + +bool +Console1::load_mapping (FILE* fin) +{ + char tmp[1024]; + PluginMapping pm; + while (fgets (tmp, 1024, fin) != NULL) { + istringstream line (tmp); + std::string token; + vector strings; + while (getline (line, token, ';')) { + boost::algorithm::trim (token); + strings.push_back (std::move (token)); + } + if (strings.size () < 2) + continue; + DEBUG_TRACE (DEBUG::Console1, + string_compose ("Console1::load_mapping - Name: '%1', Val1: '%2', Val2: '%3' \n", + strings.at (0), + strings.at (1), + strings.size () > 2 ? strings.at (2) : "")); + if (strings.at (0) == "ID") { + pm.id = strings.at (1); + } else if (strings.at (0) == "NAME") { + pm.name = strings.at (1); + } else { + try { + uint32_t index = std::stoi (strings.at (0)); + // Only store complete mappings: Indey, Name, ControllerId + if (strings.size () < 3) + continue; + PluginParameterMapping parmap; + parmap.paramIndex = index; + parmap.name = strings.at (1); + ControllerMap::const_iterator m = controllerMap.find (strings.at (2)); + if (m == controllerMap.end ()) + continue; + parmap.controllerId = m->second; + pm.parameters[index] = std::move (parmap); + } catch (std::invalid_argument&) { + continue; + } + } + } + pluginMappingMap[pm.id] = pm; + return true; +} + +void +Console1::select_plugin (uint32_t plugin_index) +{ + DEBUG_TRACE (DEBUG::Console1, "Console1::select_plugin\n"); + current_plugin_index = plugin_index; + map_select_plugin (); +} + +void +Console1::map_select_plugin () +{ + DEBUG_TRACE (DEBUG::Console1, "map_select_plugin())\n"); + bool plugin_availabe = spill_plugins (current_plugin_index); + for (uint32_t i = 0; i < bank_size; ++i) { + if (i == current_plugin_index && plugin_availabe) { + start_blinking (ControllerID (FOCUS1 + i)); + } else if (i != current_strippable_index) { + stop_blinking (ControllerID (FOCUS1 + i)); + } + } +} + +bool +Console1::spill_plugins (uint32_t plugin_index) +{ + DEBUG_TRACE (DEBUG::Console1, string_compose ("spill_plugins(%1)\n", plugin_index)); + std::shared_ptr r = std::dynamic_pointer_cast (_current_stripable); + if (!r) { + return false; + } + + // drop_ctrl_connections (); + + // switching to "Mode Track" -> calls FaderPort8::notify_fader_mode_changed() + // which drops the references, disconnects the signal and re-spills tracks + /*r->DropReferences.connect ( + processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this); + + // update when processor change + r->processors_changed.connect ( + processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::spill_plugins, this), this);*/ + + // count available + std::shared_ptr proc; + + proc = r->nth_plugin (plugin_index); + if (!proc) { + return false; + } + if (!proc->display_to_user ()) { + return false; + } + +#ifdef MIXBUS + /* don't show channelstrip plugins, use "well known" */ + if (std::dynamic_pointer_cast (proc)->is_channelstrip ()) { + continue; + } +#endif + + int n_controls = 0; + DEBUG_TRACE (DEBUG::Console1, string_compose ("Found plugin %1\n", proc->name ())); + std::shared_ptr plugin_insert = std::dynamic_pointer_cast (proc); + if (!plugin_insert) + return false; + + std::shared_ptr plugin = plugin_insert->plugin (); + if (!plugin) + return false; + + DEBUG_TRACE (DEBUG::Console1, string_compose ("Found plugin id %1\n", plugin->unique_id ())); + + PluginMappingMap::iterator pmmit = pluginMappingMap.find (plugin->unique_id ()); + if (pmmit == pluginMappingMap.end ()) { + return false; + } + + PluginMapping pluginMapping = pmmit->second; + DEBUG_TRACE (DEBUG::Console1, + string_compose ("Plugin mapping found for id %1, name %2\n", pluginMapping.id, pluginMapping.name)); + + set p = proc->what_can_be_automated (); + + for (set::iterator j = p.begin (); j != p.end (); ++j) { + std::string n = proc->describe_parameter (*j); + DEBUG_TRACE (DEBUG::Console1, string_compose ("Plugin parameter %1: %2\n", n_controls, n)); + if (n == "hidden") { + continue; + } + ParameterDescriptor parameterDescriptor; + plugin->get_parameter_descriptor (n_controls, parameterDescriptor); + if (plugin->parameter_is_input (n_controls)) { + std::shared_ptr c = + plugin_insert->automation_control (Evoral::Parameter (PluginAutomation, 0, n_controls)); + if (c) { + bool swtch = false; + if (parameterDescriptor.integer_step && parameterDescriptor.upper == 1) { + swtch = true; + } + PluginParameterMapping ppm = pluginMapping.parameters[n_controls]; + ppm.controllerId; + // c->Changed.connect (plugin_connections, MISSING_INVALIDATOR, boost::bind + // (&OSCSelectObserver::plugin_parameter_changed, this, pid, swtch, c), OSC::instance()); + // plugin_parameter_changed (pid, swtch, c); + } + } + + ++n_controls; + } + return true; +} +} \ No newline at end of file diff --git a/libs/surfaces/console1/console1.cc b/libs/surfaces/console1/console1.cc index 490771431f..5b0bb818d7 100644 --- a/libs/surfaces/console1/console1.cc +++ b/libs/surfaces/console1/console1.cc @@ -26,6 +26,7 @@ #include "ardour/audioengine.h" #include "ardour/debug.h" +#include "ardour/filesystem_paths.h" #include "ardour/meter.h" #include "ardour/monitor_control.h" #include "ardour/phase_control.h" @@ -36,8 +37,8 @@ #include "ardour/vca_manager.h" #include "console1.h" -#include "c1_gui.h" #include "c1_control.h" +#include "c1_gui.h" using namespace ARDOUR; using namespace ArdourSurface; @@ -68,7 +69,6 @@ Console1::~Console1 () BaseUI::quit (); } - void Console1::all_lights_out () { @@ -149,6 +149,7 @@ Console1::begin_using_device () f0 7d 20 00 00 00 01 00 7f 49 6f 6c 73 00 f7 */ + load_mappings (); setup_controls (); /* @@ -208,6 +209,8 @@ Console1::connect_internal_signals () { BankChange.connect (console1_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_bank, this), this); ShiftChange.connect (console1_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_shift, this, _1), this); + PluginStateChange.connect ( + console1_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_plugin_state, this, _1), this); GotoView.connect ( console1_connections, MISSING_INVALIDATOR, @@ -227,12 +230,19 @@ Console1::setup_controls () ControllerButton track_select_button ( *this, ControllerID (FOCUS1 + i), - boost::function (boost::bind (&Console1::select, this, i))); + boost::function (boost::bind (&Console1::select, this, i)), + 0, + boost::function (boost::bind (&Console1::select_plugin, this, i))); } ControllerButton shift_button ( *this, ControllerID::PRESET, boost::function (boost::bind (&Console1::shift, this, _1))); + ControllerButton plugin_state_button ( + *this, + ControllerID::TRACK_GROUP, + boost::function (boost::bind (&Console1::plugin_state, this, _1))); + ControllerButton rude_solo ( *this, ControllerID::DISPLAY_ON, boost::function (boost::bind (&Console1::rude_solo, this, _1))); ControllerButton zoom_button ( @@ -411,7 +421,10 @@ Console1::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* tb try { ControllerButton& b = get_button (ControllerID (controller_number)); - if (shift_state && b.shift_action) { + if (in_plugin_state && b.plugin_action) { + DEBUG_TRACE (DEBUG::Console1, "Executing plugin_action\n"); + b.plugin_action (value); + } else if (shift_state && b.shift_action) { DEBUG_TRACE (DEBUG::Console1, "Executing shift_action\n"); b.shift_action (value); } else { diff --git a/libs/surfaces/console1/console1.h b/libs/surfaces/console1/console1.h index 25198f1203..52fc25da4d 100644 --- a/libs/surfaces/console1/console1.h +++ b/libs/surfaces/console1/console1.h @@ -103,6 +103,9 @@ class Console1 : public MIDISurface std::string input_port_name () const override; std::string output_port_name () const override; + uint32_t load_mappings (); + bool load_mapping (FILE* fin); + /*XMLNode& get_state () const; int set_state (const XMLNode&, int version);*/ PBD::Signal0 ConnectionChange; @@ -114,7 +117,7 @@ class Console1 : public MIDISurface /* Local Signals */ PBD::Signal0 BankChange; PBD::Signal1 ShiftChange; - + PBD::Signal1 PluginStateChange; enum ControllerID { @@ -193,6 +196,81 @@ class Console1 : public MIDISurface }; + using ControllerMap = std::map; + + ControllerMap controllerMap{ { "CONTROLLER_NONE", ControllerID::CONTROLLER_NONE }, + { "VOLUME", ControllerID::VOLUME }, + { "PAN", ControllerID::PAN }, + { "MUTE", ControllerID::MUTE }, + { "SOLO", ControllerID::SOLO }, + { "ORDER", ControllerID::ORDER }, + { "DRIVE", ControllerID::DRIVE }, + { "EXTERNAL_SIDECHAIN", ControllerID::EXTERNAL_SIDECHAIN }, + { "CHARACTER", ControllerID::CHARACTER }, + { "FOCUS1", ControllerID::FOCUS1 }, + { "FOCUS2", ControllerID::FOCUS2 }, + { "FOCUS3", ControllerID::FOCUS3 }, + { "FOCUS4", ControllerID::FOCUS4 }, + { "FOCUS5", ControllerID::FOCUS5 }, + { "FOCUS6", ControllerID::FOCUS6 }, + { "FOCUS7", ControllerID::FOCUS7 }, + { "FOCUS8", ControllerID::FOCUS8 }, + { "FOCUS9", ControllerID::FOCUS9 }, + { "FOCUS10", ControllerID::FOCUS10 }, + { "FOCUS11", ControllerID::FOCUS11 }, + { "FOCUS12", ControllerID::FOCUS12 }, + { "FOCUS13", ControllerID::FOCUS13 }, + { "FOCUS14", ControllerID::FOCUS14 }, + { "FOCUS15", ControllerID::FOCUS15 }, + { "FOCUS16", ControllerID::FOCUS16 }, + { "FOCUS17", ControllerID::FOCUS17 }, + { "FOCUS18", ControllerID::FOCUS18 }, + { "FOCUS19", ControllerID::FOCUS19 }, + { "FOCUS20", ControllerID::FOCUS20 }, + { "COMP", ControllerID::COMP }, + { "COMP_THRESH", ControllerID::COMP_THRESH }, + { "COMP_RELEASE", ControllerID::COMP_RELEASE }, + { "COMP_RATIO", ControllerID::COMP_RATIO }, + { "COMP_PAR", ControllerID::COMP_PAR }, + { "COMP_ATTACK", ControllerID::COMP_ATTACK }, + { "SHAPE", ControllerID::SHAPE }, + { "SHAPE_GATE", ControllerID::SHAPE_GATE }, + { "SHAPE_SUSTAIN", ControllerID::SHAPE_SUSTAIN }, + { "SHAPE_RELEASE", ControllerID::SHAPE_RELEASE }, + { "SHAPE_PUNCH", ControllerID::SHAPE_PUNCH }, + { "PRESET", ControllerID::PRESET }, + { "HARD_GATE", ControllerID::HARD_GATE }, + { "FILTER_TO_COMPRESSORS", ControllerID::FILTER_TO_COMPRESSORS }, + { "HIGH_SHAPE", ControllerID::HIGH_SHAPE }, + { "EQ", ControllerID::EQ }, + { "HIGH_GAIN", ControllerID::HIGH_GAIN }, + { "HIGH_FREQ", ControllerID::HIGH_FREQ }, + { "HIGH_MID_GAIN", ControllerID::HIGH_MID_GAIN }, + { "HIGH_MID_FREQ", ControllerID::HIGH_MID_FREQ }, + { "HIGH_MID_SHAPE", ControllerID::HIGH_MID_SHAPE }, + { "LOW_MID_GAIN", ControllerID::LOW_MID_GAIN }, + { "LOW_MID_FREQ", ControllerID::LOW_MID_FREQ }, + { "LOW_MID_SHAPE", ControllerID::LOW_MID_SHAPE }, + { "LOW_GAIN", ControllerID::LOW_GAIN }, + { "LOW_FREQ", ControllerID::LOW_FREQ }, + { "LOW_SHAPE", ControllerID::LOW_SHAPE }, + { "PAGE_UP", ControllerID::PAGE_UP }, + { "PAGE_DOWN", ControllerID::PAGE_DOWN }, + { "DISPLAY_ON", ControllerID::DISPLAY_ON }, + { "LOW_CUT", ControllerID::LOW_CUT }, + { "MODE", ControllerID::MODE }, + { "HIGH_CUT", ControllerID::HIGH_CUT }, + { "GAIN", ControllerID::GAIN }, + { "PHASE_INV", ControllerID::PHASE_INV }, + { "INPUT_METER_L", ControllerID::INPUT_METER_L }, + { "INPUT_METER_R", ControllerID::INPUT_METER_R }, + { "OUTPUT_METER_L", ControllerID::OUTPUT_METER_L }, + { "OUTPUT_METER_R", ControllerID::OUTPUT_METER_R }, + { "SHAPE_METER", ControllerID::SHAPE_METER }, + { "COMP_METER", ControllerID::COMP_METER }, + { "TRACK_COPY", ControllerID::TRACK_COPY }, + { "TRACK_GROUP", ControllerID::TRACK_GROUP } }; + private: /* GUI */ mutable C1GUI* gui; @@ -201,12 +279,16 @@ class Console1 : public MIDISurface /* Configuration */ const uint32_t bank_size = 20; + // Shift button bool shift_state = false; + bool in_plugin_state = false; bool rolling = false; uint32_t current_bank = 0; uint32_t current_strippable_index = 0; + uint32_t current_plugin_index = 0; + std::shared_ptr current_pan_control = nullptr; std::shared_ptr _current_stripable; @@ -323,6 +405,7 @@ class Console1 : public MIDISurface void rude_solo (const uint32_t); void select (const uint32_t i); void shift (const uint32_t); + void plugin_state (const uint32_t); void solo (const uint32_t); void trim (const uint32_t value); void window (const uint32_t value); @@ -418,6 +501,7 @@ class Console1 : public MIDISurface void map_recenable (); void map_select (); void map_shift (bool shift); + void map_plugin_state (bool state); void map_solo (); void map_trim (); @@ -463,6 +547,33 @@ class Console1 : public MIDISurface float calculate_meter (float dB); uint32_t control_to_midi (Controllable controllable, float val, uint32_t max_value_for_type = 127); float midi_to_control (Controllable controllable, uint32_t val, uint32_t max_value_for_type = 127); + + struct PluginParameterMapping + { + int paramIndex; + std::string name; + ControllerID controllerId; + }; + + using ParameterMap = std::map; + + struct PluginMapping + { + std::string id; + std::string name; + ParameterMap parameters; + }; + + /* plugin handling */ + bool spill_plugins (uint32_t plugin_index); + + /* plugin operations */ + void select_plugin (const uint32_t i); + + void map_select_plugin (); + + using PluginMappingMap = std::map; + PluginMappingMap pluginMappingMap; }; } #endif /* ardour_surface_console1_h */ \ No newline at end of file diff --git a/libs/surfaces/console1/wscript b/libs/surfaces/console1/wscript index 36a18f6024..9f61c3676a 100644 --- a/libs/surfaces/console1/wscript +++ b/libs/surfaces/console1/wscript @@ -18,6 +18,7 @@ def build(bld): console1_interface.cc console1.cc c1_operations.cc + c1_plugin_operations.cc c1_gui.cc ''' obj.defines = [ 'PACKAGE="ardour_console1"' ]