From 1dc879eb8d283f3abcee7825bef0346ca9229552 Mon Sep 17 00:00:00 2001 From: Hoger Dehnhardt Date: Sun, 7 May 2023 16:21:37 +0200 Subject: [PATCH] Softube Console1 add surface (libs) --- libs/ardour/ardour/debug.h | 1 + libs/ardour/debug.cc | 1 + libs/ardour/globals.cc | 2 + libs/ardour/port_manager.cc | 1 + libs/surfaces/console1/c1_control.h | 181 +++ libs/surfaces/console1/c1_gui.cc | 270 +++++ libs/surfaces/console1/c1_gui.h | 84 ++ libs/surfaces/console1/c1_operations.cc | 1110 +++++++++++++++++ libs/surfaces/console1/console1.cc | 1120 ++++++++++++++++++ libs/surfaces/console1/console1.h | 468 ++++++++ libs/surfaces/console1/console1_interface.cc | 61 + libs/surfaces/console1/wscript | 33 + libs/surfaces/wscript | 2 + 13 files changed, 3334 insertions(+) create mode 100644 libs/surfaces/console1/c1_control.h create mode 100644 libs/surfaces/console1/c1_gui.cc create mode 100644 libs/surfaces/console1/c1_gui.h create mode 100644 libs/surfaces/console1/c1_operations.cc create mode 100644 libs/surfaces/console1/console1.cc create mode 100644 libs/surfaces/console1/console1.h create mode 100644 libs/surfaces/console1/console1_interface.cc create mode 100644 libs/surfaces/console1/wscript diff --git a/libs/ardour/ardour/debug.h b/libs/ardour/ardour/debug.h index 23c18869e5..9a682a8930 100644 --- a/libs/ardour/ardour/debug.h +++ b/libs/ardour/ardour/debug.h @@ -48,6 +48,7 @@ namespace PBD { LIBARDOUR_API extern DebugBits CC121; LIBARDOUR_API extern DebugBits CaptureAlignment; LIBARDOUR_API extern DebugBits ChanMapping; + LIBARDOUR_API extern DebugBits Console1; LIBARDOUR_API extern DebugBits ContourDesignControl; LIBARDOUR_API extern DebugBits ControlProtocols; LIBARDOUR_API extern DebugBits CycleTimers; diff --git a/libs/ardour/debug.cc b/libs/ardour/debug.cc index cbe22b38ff..d81138ae79 100644 --- a/libs/ardour/debug.cc +++ b/libs/ardour/debug.cc @@ -43,6 +43,7 @@ PBD::DebugBits PBD::DEBUG::Butler = PBD::new_debug_bit ("Butler"); PBD::DebugBits PBD::DEBUG::CC121 = PBD::new_debug_bit ("cc121"); PBD::DebugBits PBD::DEBUG::CaptureAlignment = PBD::new_debug_bit ("capturealignment"); PBD::DebugBits PBD::DEBUG::ChanMapping = PBD::new_debug_bit ("chanmapping"); +PBD::DebugBits PBD::DEBUG::Console1 = PBD::new_debug_bit ("console1"); PBD::DebugBits PBD::DEBUG::ContourDesignControl = PBD::new_debug_bit ("contourdesigncontrol"); PBD::DebugBits PBD::DEBUG::ControlProtocols = PBD::new_debug_bit ("controlprotocols"); PBD::DebugBits PBD::DEBUG::CycleTimers = PBD::new_debug_bit ("cycletimers"); diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index bd0cea4d92..52399d9bf0 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -699,6 +699,8 @@ ARDOUR::init (bool try_optimization, const char* localedir, bool with_gui) reserved_io_names[_("FaderPort8 Send")] = false; reserved_io_names[_("FaderPort16 Recv")] = false; reserved_io_names[_("FaderPort16 Send")] = false; + reserved_io_names[_("Console1 Recv")] = false; + reserved_io_names[_("Console1 Send")] = false; MIDI::Name::MidiPatchManager::instance ().load_midnams_in_thread (); diff --git a/libs/ardour/port_manager.cc b/libs/ardour/port_manager.cc index a15fb43b55..8055f3ec4a 100644 --- a/libs/ardour/port_manager.cc +++ b/libs/ardour/port_manager.cc @@ -1477,6 +1477,7 @@ PortManager::port_is_control_only (std::string const& name) X_(".*US-2400 .*"), X_(".*Mackie .*"), X_(".*MIDI Control .*"), + X_(".*Console1 .*"), }; pattern = "("; diff --git a/libs/surfaces/console1/c1_control.h b/libs/surfaces/console1/c1_control.h new file mode 100644 index 0000000000..4c01cb4705 --- /dev/null +++ b/libs/surfaces/console1/c1_control.h @@ -0,0 +1,181 @@ +#ifndef ardour_surface_console1_button_h +#define ardour_surface_console1_button_h + +#include "ardour/debug.h" +#include "console1.h" + +namespace ArdourSurface { + +using namespace PBD; + +using ControllerID = Console1::ControllerID; + +class Controller +{ + public: + enum ControllerType + { + CONTROLLER, + CONTROLLER_BUTTON, + MULTISTATE_BUTTON, + ENCODER, + METER + }; + + Controller (Console1& console1, ControllerID id) + : console1 (console1) + , _id (id) + { + } + + virtual ~Controller () {} + + Console1& console1; + ControllerID id () const { return _id; } + + virtual ControllerType get_type () { return CONTROLLER; } + + protected: + ControllerID _id; +}; + +class ControllerButton : public Controller +{ + public: + ControllerButton (Console1& console1, + ControllerID id, + boost::function action, + boost::function shift_action = 0) + : Controller (console1, id) + , action (action) + , shift_action (shift_action) + { + console1.buttons.insert (std::make_pair (id, *this)); + } + + ControllerType get_type () { return CONTROLLER_BUTTON; } + + virtual void set_led_state (bool onoff) + { + // DEBUG_TRACE(DEBUG::Console1, "ControllerButton::set_led_state ...\n"); + MIDI::byte buf[3]; + buf[0] = 0xB0; + buf[1] = _id; + buf[2] = onoff ? 127 : 0; + + console1.write (buf, 3); + } + + virtual void set_led_value (uint32_t val) + { + // DEBUG_TRACE(DEBUG::Console1, "ControllerButton::set_led_state ...\n"); + MIDI::byte buf[3]; + buf[0] = 0xB0; + buf[1] = _id; + buf[2] = val; + + console1.write (buf, 3); + } + boost::function action; + boost::function shift_action; +}; + +class MultiStateButton : public Controller +{ + public: + MultiStateButton (Console1& console1, + ControllerID id, + std::vector state_values, + boost::function action, + boost::function shift_action = 0) + : Controller (console1, id) + , action (action) + , shift_action (shift_action) + , state_values (state_values) + { + console1.multi_buttons.insert (std::make_pair (id, *this)); + } + + ControllerType get_type () { return MULTISTATE_BUTTON; } + + virtual void set_led_state (uint32_t state) + { + if (state >= state_values.size ()) + return; + MIDI::byte buf[3]; + buf[0] = 0xB0; + buf[1] = _id; + buf[2] = state_values[state]; + + console1.write (buf, 3); + } + + uint32_t state_count () { return state_values.size (); } + + boost::function action; + boost::function shift_action; + + private: + std::vector state_values; +}; + +class Meter : public Controller +{ + public: + Meter (Console1& console1, + ControllerID id, + boost::function action, + boost::function shift_action = 0) + : Controller (console1, id) + , action (action) + , shift_action (shift_action) + { + console1.meters.insert (std::make_pair (id, *this)); + } + + ControllerType get_type () { return METER; } + + virtual void set_value (uint32_t value) + { + MIDI::byte buf[3]; + buf[0] = 0xB0; + buf[1] = _id; + buf[2] = value; + + console1.write (buf, 3); + } + boost::function action; + boost::function shift_action; +}; + +class Encoder : public Controller +{ + public: + Encoder (Console1& console1, + ControllerID id, + boost::function action, + boost::function shift_action = 0) + : Controller (console1, id) + , action (action) + , shift_action (shift_action) + { + console1.encoders.insert (std::make_pair (id, *this)); + } + + ControllerType get_type () { return ENCODER; } + + virtual void set_value (uint32_t value) + { + MIDI::byte buf[3]; + buf[0] = 0xB0; + buf[1] = _id; + buf[2] = value; + + console1.write (buf, 3); + } + boost::function action; + boost::function shift_action; +}; + +} +#endif // ardour_surface_console1_button_h \ No newline at end of file diff --git a/libs/surfaces/console1/c1_gui.cc b/libs/surfaces/console1/c1_gui.cc new file mode 100644 index 0000000000..d45811beaa --- /dev/null +++ b/libs/surfaces/console1/c1_gui.cc @@ -0,0 +1,270 @@ +/* + * 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 "pbd/unwind.h" +#include "pbd/strsplit.h" +#include "pbd/file_utils.h" + +#include "gtkmm2ext/bindings.h" +#include "gtkmm2ext/gui_thread.h" +#include "gtkmm2ext/utils.h" + +#include "ardour/audioengine.h" +#include "ardour/filesystem_paths.h" +#include "ardour/parameter_descriptor.h" +#include "ardour/debug.h" + +#include "console1.h" +#include "c1_gui.h" + +#include "pbd/i18n.h" + +using namespace PBD; +using namespace ARDOUR; +using namespace ArdourSurface; +using namespace std; +using namespace Gtk; +using namespace Gtkmm2ext; + +void* +Console1::get_gui () const +{ + if (!gui) { + const_cast(this)->build_gui (); + } + static_cast(gui)->show_all(); + return gui; +} + +void +Console1::tear_down_gui () +{ + if (gui) { + Gtk::Widget *w = static_cast(gui)->get_parent(); + if (w) { + w->hide(); + delete w; + } + } + delete gui; + gui = 0; +} + +void +Console1::build_gui () +{ + gui = new C1GUI (*this); +} + +/*--------------------*/ + +C1GUI::C1GUI (Console1& p) + : c1 (p) + , table (2, 5) + , action_table (5, 4) + , ignore_active_change (false) +{ + set_border_width (12); + + table.set_row_spacings (4); + table.set_col_spacings (6); + table.set_border_width (12); + table.set_homogeneous (false); + + std::string data_file_path; + string name = "console1.png"; + Searchpath spath(ARDOUR::ardour_data_search_path()); + spath.add_subdirectory_to_paths ("icons"); + find_file (spath, name, data_file_path); + if (!data_file_path.empty()) { + image.set (data_file_path); + hpacker.pack_start (image, false, false); + } + + Gtk::Label* l; + Gtk::Alignment* align; + int row = 0; + + input_combo.pack_start (midi_port_columns.short_name); + output_combo.pack_start (midi_port_columns.short_name); + + input_combo.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &C1GUI::active_port_changed), &input_combo, true)); + output_combo.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &C1GUI::active_port_changed), &output_combo, false)); + + l = manage (new Gtk::Label); + l->set_markup (string_compose ("%1", _("Incoming MIDI on:"))); + l->set_alignment (1.0, 0.5); + table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0)); + table.attach (input_combo, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0); + row++; + + l = manage (new Gtk::Label); + l->set_markup (string_compose ("%1", _("Outgoing MIDI on:"))); + l->set_alignment (1.0, 0.5); + table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0)); + table.attach (output_combo, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0); + row++; + + hpacker.pack_start (table, true, true); + + set_spacing (12); + + pack_start (hpacker, false, false); + + /* update the port connection combos */ + + update_port_combos (); + + /* catch future changes to connection state */ + + ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (_port_connections, invalidator (*this), boost::bind (&C1GUI::connection_handler, this), gui_context()); + ARDOUR::AudioEngine::instance()->PortPrettyNameChanged.connect (_port_connections, invalidator (*this), boost::bind (&C1GUI::connection_handler, this), gui_context()); + c1.ConnectionChange.connect (_port_connections, invalidator (*this), boost::bind (&C1GUI::connection_handler, this), gui_context()); +} + +C1GUI::~C1GUI () +{ +} + +void +C1GUI::connection_handler () +{ + /* ignore all changes to combobox active strings here, because we're + updating them to match a new ("external") reality - we were called + because port connections have changed. + */ + + PBD::Unwinder ici (ignore_active_change, true); + + update_port_combos (); +} + +void +C1GUI::update_port_combos () +{ + vector midi_inputs; + vector midi_outputs; + + ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsOutput|ARDOUR::IsTerminal), midi_inputs); + ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsInput|ARDOUR::IsTerminal), midi_outputs); + + Glib::RefPtr input = build_midi_port_list (midi_inputs, true); + Glib::RefPtr output = build_midi_port_list (midi_outputs, false); + bool input_found = false; + bool output_found = false; + int n; + + input_combo.set_model (input); + output_combo.set_model (output); + + Gtk::TreeModel::Children children = input->children(); + Gtk::TreeModel::Children::iterator i; + i = children.begin(); + ++i; /* skip "Disconnected" */ + + + for (n = 1; i != children.end(); ++i, ++n) { + string port_name = (*i)[midi_port_columns.full_name]; + if (c1.input_port()->connected_to (port_name)) { + input_combo.set_active (n); + input_found = true; + break; + } + } + + if (!input_found) { + input_combo.set_active (0); /* disconnected */ + } + + children = output->children(); + i = children.begin(); + ++i; /* skip "Disconnected" */ + + for (n = 1; i != children.end(); ++i, ++n) { + string port_name = (*i)[midi_port_columns.full_name]; + if (c1.output_port()->connected_to (port_name)) { + output_combo.set_active (n); + output_found = true; + break; + } + } + + if (!output_found) { + output_combo.set_active (0); /* disconnected */ + } +} + +Glib::RefPtr +C1GUI::build_midi_port_list (vector const & ports, bool for_input) +{ + Glib::RefPtr store = ListStore::create (midi_port_columns); + TreeModel::Row row; + + row = *store->append (); + row[midi_port_columns.full_name] = string(); + row[midi_port_columns.short_name] = _("Disconnected"); + + for (vector::const_iterator p = ports.begin(); p != ports.end(); ++p) { + row = *store->append (); + row[midi_port_columns.full_name] = *p; + std::string pn = ARDOUR::AudioEngine::instance()->get_pretty_name_by_name (*p); + if (pn.empty ()) { + pn = (*p).substr ((*p).find (':') + 1); + } + row[midi_port_columns.short_name] = pn; + } + + return store; +} + +void +C1GUI::active_port_changed (Gtk::ComboBox* combo, bool for_input) +{ + if (ignore_active_change) { + return; + } + + TreeModel::iterator active = combo->get_active (); + string new_port = (*active)[midi_port_columns.full_name]; + + if (new_port.empty()) { + if (for_input) { + c1.input_port()->disconnect_all (); + } else { + c1.output_port()->disconnect_all (); + } + + return; + } + + if (for_input) { + if (!c1.input_port()->connected_to (new_port)) { + c1.input_port()->disconnect_all (); + c1.input_port()->connect (new_port); + } + } else { + if (!c1.output_port()->connected_to (new_port)) { + c1.output_port()->disconnect_all (); + c1.output_port()->connect (new_port); + } + } +} diff --git a/libs/surfaces/console1/c1_gui.h b/libs/surfaces/console1/c1_gui.h new file mode 100644 index 0000000000..afd3e0f9c5 --- /dev/null +++ b/libs/surfaces/console1/c1_gui.h @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#ifndef __ardour_console1_gui_h__ +#define __ardour_console1_gui_h__ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Gtk { + class CellRendererCombo; + class ListStore; +} + +#include "ardour/mode.h" + +#include "console1.h" + +namespace ArdourSurface { + +class C1GUI : public Gtk::VBox +{ +public: + C1GUI (Console1&); + ~C1GUI (); + +private: + Console1& c1; + PBD::ScopedConnectionList lcxl_connections; + Gtk::VBox hpacker; + Gtk::Table table; + Gtk::Table action_table; + Gtk::ComboBox input_combo; + Gtk::ComboBox output_combo; + Gtk::Image image; + + 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); + add (full_name); + } + Gtk::TreeModelColumn short_name; + Gtk::TreeModelColumn full_name; + }; + + MidiPortColumns midi_port_columns; + 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); + +}; + +} + +#endif /* __ardour_console1_gui_h__ */ diff --git a/libs/surfaces/console1/c1_operations.cc b/libs/surfaces/console1/c1_operations.cc new file mode 100644 index 0000000000..6925e8d2f0 --- /dev/null +++ b/libs/surfaces/console1/c1_operations.cc @@ -0,0 +1,1110 @@ +/* + * 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 "ardour/meter.h" +#include "ardour/monitor_control.h" +#include "ardour/phase_control.h" +#include "ardour/presentation_info.h" +#include "ardour/session.h" +#include "c1_control.h" +#include "console1.h" + +using namespace ARDOUR; +using namespace ArdourSurface; +using namespace PBD; +using namespace Glib; +using namespace std; + +/* Operations */ + +void +Console1::bank (bool up) +{ + DEBUG_TRACE (DEBUG::Console1, "Console1::page\n"); + bool changed = false; + uint32_t list_size = strip_inventory.size (); + + if (up) { + if ((current_bank + 1) * bank_size < list_size) { + changed = true; + ++current_bank; + } + } else { + if (current_bank > 0) { + changed = true; + --current_bank; + } + } + if (changed) { + uint32_t new_index = current_bank * bank_size + current_strippable_index; + if (new_index > (list_size - 1)) + new_index = list_size - 1; + select_rid_by_index (new_index); + void BankChange (); + } +} + +void +Console1::gain (const uint32_t value) +{ + if (!_current_stripable) { + return; + } + std::shared_ptr control = _current_stripable->gain_control (); + double gain = midi_to_control (control, value); + session->set_control (control, gain, PBD::Controllable::UseGroup); +} + +void +Console1::mute (const uint32_t) +{ + DEBUG_TRACE (DEBUG::Console1, "Console1::mute ...\n"); + if (!_current_stripable) { + return; + } + + if (_current_stripable == session->monitor_out ()) { + std::shared_ptr mp = _current_stripable->monitor_control (); + mp->set_cut_all (!mp->cut_all ()); + return; + } + + _current_stripable->mute_control ()->set_value (!_current_stripable->mute_control ()->muted (), + PBD::Controllable::UseGroup); +} + +void +Console1::pan (const uint32_t value) +{ + if (!_current_stripable) { + return; + } + if (current_pan_control) { + std::shared_ptr control = current_pan_control; + double pan = midi_to_control (control, value); + session->set_control (control, pan, PBD::Controllable::UseGroup); + } +} + +void +Console1::phase (const uint32_t value) +{ + DEBUG_TRACE (DEBUG::Console1, "phase() \n"); + if (!_current_stripable || !_current_stripable->phase_control ()) { + return; + } + bool inverted = _current_stripable->phase_control ()->inverted (0); + for (uint64_t i = 0; i < _current_stripable->phase_control ()->size (); i++) { + _current_stripable->phase_control ()->set_phase_invert (i, !inverted); + } +} + +void +Console1::rude_solo (const uint32_t value) +{ + + DEBUG_TRACE (DEBUG::Console1, "rude_solo() \n"); + if (!value) { + session->cancel_all_solo (); + } else { + try { + get_button (ControllerID::DISPLAY_ON).set_led_state (false); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } + } +} + +void +Console1::select (const uint32_t i) +{ + uint32_t strip_index = current_bank * bank_size + i; + DEBUG_TRACE (DEBUG::Console1, string_compose ("select( %1 ) : idx %2\n", i, strip_index)); + select_rid_by_index (strip_index); +} + +void +Console1::shift (const uint32_t val) +{ + DEBUG_TRACE (DEBUG::Console1, "shift()\n"); + shift_state = !shift_state; + ShiftChange (val); +} + +void +Console1::solo (const uint32_t) +{ + DEBUG_TRACE (DEBUG::Console1, "Console1::solo())\n"); + if (!_current_stripable) { + return; + } + + session->set_control (_current_stripable->solo_control (), + !_current_stripable->solo_control ()->self_soloed (), + PBD::Controllable::UseGroup); +} + +void +Console1::trim (const uint32_t value) +{ + if (!_current_stripable) { + return; + } + std::shared_ptr control = _current_stripable->trim_control (); + double trim = midi_to_control (control, value); + session->set_control (control, trim, PBD::Controllable::UseGroup); +} + +void +Console1::window (const uint32_t value) +{ + DEBUG_TRACE (DEBUG::Console1, "window()\n"); + switch (value) { + case 0: + access_action ("Common/show-editor"); + break; + case 63: + access_action ("Common/show-mixer"); + break; + case 127: + access_action ("Common/show-trigger"); + break; + default: + break; + } +} + +void +Console1::zoom (const uint32_t value) +{ + DEBUG_TRACE (DEBUG::Console1, "zoom()\n"); + access_action ("Editor/zoom-to-selection"); +} + +// Filter Section +void +Console1::filter (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->filter_enable_controllable (true)) + return; + session->set_control ( + _current_stripable->filter_enable_controllable (true), value > 0, PBD::Controllable::UseGroup); +} + +void +Console1::low_cut (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->filter_freq_controllable (true)) { + return; + } + std::shared_ptr control = _current_stripable->filter_freq_controllable (true); + double freq = midi_to_control (control, value); + session->set_control (control, freq, PBD::Controllable::UseGroup); +} + +void +Console1::high_cut (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->filter_freq_controllable (false)) { + return; + } + std::shared_ptr control = _current_stripable->filter_freq_controllable (false); + double freq = midi_to_control (control, value); + session->set_control (control, freq, PBD::Controllable::UseGroup); +} + +// Gate Section +void +Console1::gate (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->gate_enable_controllable ()) + return; + session->set_control (_current_stripable->gate_enable_controllable (), value > 0, PBD::Controllable::UseGroup); +} + +void +Console1::gate_scf (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->gate_key_filter_enable_controllable ()) + return; + session->set_control ( + _current_stripable->gate_key_filter_enable_controllable (), value > 0, PBD::Controllable::UseGroup); +} + +void +Console1::gate_listen (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->gate_key_listen_controllable ()) + return; + session->set_control (_current_stripable->gate_key_listen_controllable (), value > 0, PBD::Controllable::UseGroup); +} + +void +Console1::gate_thresh (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->gate_threshold_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->gate_threshold_controllable (); + double freq = midi_to_control (control, value); + session->set_control (control, freq, PBD::Controllable::UseGroup); +} + +void +Console1::gate_depth (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->gate_depth_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->gate_depth_controllable (); + double freq = midi_to_control (control, value); + session->set_control (control, freq, PBD::Controllable::UseGroup); +} + +void +Console1::gate_release (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->gate_release_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->gate_release_controllable (); + double freq = midi_to_control (control, value); + session->set_control (control, freq, PBD::Controllable::UseGroup); +} + +void +Console1::gate_attack (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->gate_attack_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->gate_attack_controllable (); + double freq = midi_to_control (control, value); + session->set_control (control, freq, PBD::Controllable::UseGroup); +} + +void +Console1::gate_hyst (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->gate_hysteresis_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->gate_hysteresis_controllable (); + double freq = midi_to_control (control, value); + session->set_control (control, freq, PBD::Controllable::UseGroup); +} + +void +Console1::gate_hold (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->gate_hold_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->gate_hold_controllable (); + double freq = midi_to_control (control, value); + session->set_control (control, freq, PBD::Controllable::UseGroup); +} + +void +Console1::gate_filter_freq (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->gate_key_filter_freq_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->gate_key_filter_freq_controllable (); + double freq = midi_to_control (control, value); + session->set_control (control, freq, PBD::Controllable::UseGroup); +} + +// EQ-Section + +void +Console1::eq (const uint32_t value) +{ + DEBUG_TRACE (DEBUG::Console1, "EQ ...\n"); + if (!_current_stripable) { + return; + } + if (_current_stripable->eq_enable_controllable ()) + session->set_control (_current_stripable->eq_enable_controllable (), value > 0, PBD::Controllable::UseGroup); + else + map_eq (); +} + +void +Console1::eq_low_shape (const uint32_t value) +{ + DEBUG_TRACE (DEBUG::Console1, string_compose ("EQ eq_low_shape( %1 )\n", value)); + if (!_current_stripable) { + return; + } + if (_current_stripable->eq_shape_controllable (0)) + session->set_control (_current_stripable->eq_shape_controllable (0), value > 0, PBD::Controllable::UseGroup); + else + map_eq_low_shape (); +} + +void +Console1::eq_high_shape (const uint32_t value) +{ + DEBUG_TRACE (DEBUG::Console1, "EQ eq_high_shape...\n"); + if (!_current_stripable) { + return; + } + if (_current_stripable->eq_shape_controllable (3)) + session->set_control (_current_stripable->eq_shape_controllable (3), value > 0, PBD::Controllable::UseGroup); + else + map_eq_high_shape (); +} + +void +Console1::eq_freq (const uint32_t band, uint32_t value) +{ + if (!_current_stripable || !_current_stripable->eq_freq_controllable (band)) { + return; + } + std::shared_ptr control = _current_stripable->eq_freq_controllable (band); + double freq = midi_to_control (control, value); + session->set_control (control, freq, PBD::Controllable::UseGroup); +} + +void +Console1::eq_gain (const uint32_t band, uint32_t value) +{ + if (!_current_stripable || !_current_stripable->eq_gain_controllable (band)) { + return; + } + std::shared_ptr control = _current_stripable->eq_gain_controllable (band); + double gain = midi_to_control (control, value); + session->set_control (control, gain, PBD::Controllable::UseGroup); +} + +// The Mixbus-Sends are in the EQ section +// Without Shift: +// LowMid Shape is Send 11 +// HighMid Shape is Send 12 +// +// With Shift +// LowMid Shape is Send 9 +// HighMid Shape is Send 10 +// And the rest is +// Send 01 02 03 04 +// Send 05 06 07 08 + +void +Console1::mb_send_level (const uint32_t n, const uint32_t value) +{ + uint32_t n_offset = n; +#ifdef MIXBUS + if (_current_stripable->presentation_info ().flags () & PresentationInfo::Flag::Mixbus) { + n_offset = n - 8; + } +#endif + if (!_current_stripable || !_current_stripable->send_level_controllable (n_offset)) { + return; + } + + std::shared_ptr control = _current_stripable->send_level_controllable (n_offset); + double gain = midi_to_control (control, value); + session->set_control (control, gain, PBD::Controllable::UseGroup); + if (value == 0) { + std::shared_ptr enable_control = _current_stripable->send_enable_controllable (n_offset); + if (enable_control) { + session->set_control (enable_control, 0, PBD::Controllable::UseGroup); + } + } +} + +// Drive Button +void +Console1::drive (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->tape_drive_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->tape_drive_controllable (); + if (_current_stripable->presentation_info ().flags () & PresentationInfo::AudioTrack) { + DEBUG_TRACE (DEBUG::Console1, string_compose ("drive audio track %1\n", value)); + session->set_control (control, value > 62 ? 1 : 0, PBD::Controllable::UseGroup); + } else { + double gain = midi_to_control (control, value); + session->set_control (control, gain, PBD::Controllable::UseGroup); + } +} + +// Comp Section +void +Console1::comp (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->comp_enable_controllable ()) + return; + session->set_control (_current_stripable->comp_enable_controllable (), value > 0, PBD::Controllable::UseGroup); +} + +void +Console1::comp_mode (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->comp_mode_controllable ()) + return; + int new_val = value == 63 ? 1 : value == 127 ? 2 : 0; + session->set_control (_current_stripable->comp_mode_controllable (), new_val, PBD::Controllable::UseGroup); +} + +void +Console1::comp_thresh (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->comp_threshold_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->comp_threshold_controllable (); + double gain = midi_to_control (control, value); + session->set_control (control, gain, PBD::Controllable::UseGroup); +} + +void +Console1::comp_attack (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->comp_attack_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->comp_attack_controllable (); + double gain = midi_to_control (control, value); + session->set_control (control, gain, PBD::Controllable::UseGroup); +} + +void +Console1::comp_release (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->comp_release_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->comp_release_controllable (); + double gain = midi_to_control (control, value); + session->set_control (control, gain, PBD::Controllable::UseGroup); +} + +void +Console1::comp_ratio (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->comp_ratio_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->comp_ratio_controllable (); + double gain = midi_to_control (control, value); + session->set_control (control, gain, PBD::Controllable::UseGroup); +} + +void +Console1::comp_makeup (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->comp_makeup_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->comp_makeup_controllable (); + double gain = midi_to_control (control, value); + session->set_control (control, gain, PBD::Controllable::UseGroup); +} + +void +Console1::comp_emph (const uint32_t value) +{ + if (!_current_stripable || !_current_stripable->comp_key_filter_freq_controllable ()) { + return; + } + std::shared_ptr control = _current_stripable->comp_key_filter_freq_controllable (); + double gain = midi_to_control (control, value); + session->set_control (control, gain, PBD::Controllable::UseGroup); +} + +/* ********************************************************** +************************** Mappings ************************* +*************************************************************/ +void +Console1::map_bank () +{ + uint32_t list_size = strip_inventory.size (); + try { + get_button (PAGE_UP).set_led_state (list_size > (current_bank + 1) * bank_size); + get_button (PAGE_DOWN).set_led_state (current_bank > 0); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } +} + +void +Console1::map_gain () +{ + ControllerID controllerID = ControllerID::VOLUME; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->gain_control (); + map_encoder (controllerID, control); + } +} + +void +Console1::map_monitoring () +{ + if (_current_stripable && _current_stripable->monitoring_control ()) { + std::shared_ptr control = _current_stripable->monitoring_control (); + monitor_state = control->monitoring_state (); + } else { + monitor_state = ARDOUR::MonitorState::MonitoringSilence; + } +} + +void +Console1::map_mute () +{ + DEBUG_TRACE (DEBUG::Console1, "Console1::map_mute ...\n"); + if (_current_stripable) { + if (_current_stripable->mute_control ()->muted ()) { + get_button (MUTE).set_led_state (true); + } else if (_current_stripable->mute_control ()->muted_by_others_soloing () || + _current_stripable->mute_control ()->muted_by_masters ()) { + + DEBUG_TRACE (DEBUG::Console1, "Console1::map_mute start blinking\n"); + start_blinking (MUTE); + } else { + DEBUG_TRACE (DEBUG::Console1, "Console1::map_mute stop blinking\n"); + stop_blinking (MUTE); + } + } else { + DEBUG_TRACE (DEBUG::Console1, "Console1::map_mute stop blinking 2\n"); + stop_blinking (MUTE); + } +} + +void +Console1::map_pan () +{ + ControllerID controllerID = ControllerID::PAN; + if (map_encoder (controllerID)) { + std::shared_ptr control = current_pan_control; + map_encoder (controllerID, control); + } +} + +void +Console1::map_phase () +{ + DEBUG_TRACE (DEBUG::Console1, "map_phase \n"); + ControllerButton& controllerButton = static_cast (get_button (PHASE_INV)); + if (_current_stripable) { + uint32_t channels = _current_stripable->phase_control ()->size (); + uint32_t inverted = 0; + for (uint32_t i = 0; i < channels; ++i) { + if (_current_stripable->phase_control ()->inverted (i)) + ++inverted; + } + if (inverted == 0) { + stop_blinking (PHASE_INV); + controllerButton.set_led_state (false); + } else if (inverted == channels) { + stop_blinking (PHASE_INV); + controllerButton.set_led_state (true); + } else + start_blinking (PHASE_INV); + } else { + controllerButton.set_led_state (false); + } +} + +void +Console1::map_recenable () +{ + DEBUG_TRACE (DEBUG::Console1, "map_recenable()\n"); + if (!_current_stripable) + strip_recenabled = false; + else if (_current_stripable->rec_enable_control ()) { + strip_recenabled = _current_stripable->rec_enable_control ()->get_value (); + } +} + +void +Console1::map_select () +{ + DEBUG_TRACE (DEBUG::Console1, "map_select())\n"); + for (uint32_t i = 0; i < bank_size; ++i) { + get_button (ControllerID (FOCUS1 + i)).set_led_state (i == current_strippable_index); + } +} + +void +Console1::map_shift (bool shift) +{ + DEBUG_TRACE (DEBUG::Console1, "map_shift()\n"); + try { + ControllerButton& controllerButton = static_cast (get_button (PRESET)); + controllerButton.set_led_state (shift); + map_stripable_state (); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } +} + +void +Console1::map_solo () +{ + DEBUG_TRACE (DEBUG::Console1, "map_solo()\n"); + try { + ControllerButton& controllerButton = static_cast (get_button (SOLO)); + if (_current_stripable) { + controllerButton.set_led_state (_current_stripable->solo_control ()->soloed ()); + } else { + controllerButton.set_led_state (false); + } + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } +} + +void +Console1::map_trim () +{ + ControllerID controllerID = ControllerID::GAIN; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->trim_control (); + map_encoder (controllerID, control); + } +} + +// Filter Section +void +Console1::map_filter () +{ + if (!_current_stripable) { + return; + } + try { + get_button (ControllerID::FILTER_TO_COMPRESSORS) + .set_led_state (_current_stripable->filter_enable_controllable (true) + ? _current_stripable->filter_enable_controllable (true)->get_value () + : false); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } +} + +void +Console1::map_low_cut () +{ + ControllerID controllerID = ControllerID::LOW_CUT; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->filter_freq_controllable (true); + map_encoder (controllerID, control); + } +} + +void +Console1::map_high_cut () +{ + ControllerID controllerID = ControllerID::HIGH_CUT; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->filter_freq_controllable (false); + map_encoder (controllerID, control); + } +} + +// Gate Section +void +Console1::map_gate () +{ + if (!_current_stripable) + return; + try { + get_button (ControllerID::SHAPE) + .set_led_state (_current_stripable->gate_enable_controllable () + ? _current_stripable->gate_enable_controllable ()->get_value () + : false); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } +} + +void +Console1::map_gate_scf () +{ + if (!_current_stripable || shift_state) + return; + try { + DEBUG_TRACE (DEBUG::Console1, string_compose ("map_gate_scf() - shift: %1\n", shift_state)); + get_button (ControllerID::HARD_GATE) + .set_led_state (_current_stripable->gate_key_filter_enable_controllable () + ? _current_stripable->gate_key_filter_enable_controllable ()->get_value () + : false); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } +} + +void +Console1::map_gate_listen () +{ + if (!_current_stripable || !shift_state) + return; + try { + DEBUG_TRACE (DEBUG::Console1, string_compose ("map_gate_listen() - shift: %1\n", shift_state)); + get_button (ControllerID::HARD_GATE) + .set_led_state (_current_stripable->gate_key_listen_controllable () + ? _current_stripable->gate_key_listen_controllable ()->get_value () + : false); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } +} + +void +Console1::map_gate_thresh () +{ + ControllerID controllerID = ControllerID::SHAPE_GATE; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->gate_threshold_controllable (); + map_encoder (controllerID, control); + } +} + +void +Console1::map_gate_release () +{ + if (shift_state) { + return; + } + ControllerID controllerID = ControllerID::SHAPE_RELEASE; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->gate_release_controllable (); + map_encoder (controllerID, control); + } +} + +void +Console1::map_gate_attack () +{ + if (shift_state) { + return; + } + ControllerID controllerID = ControllerID::SHAPE_SUSTAIN; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->gate_attack_controllable (); + map_encoder (controllerID, control); + } +} + +void +Console1::map_gate_depth () +{ + if (shift_state) { + return; + } + ControllerID controllerID = ControllerID::SHAPE_PUNCH; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->gate_depth_controllable (); + map_encoder (controllerID, control); + } +} + +void +Console1::map_gate_hyst () +{ + if (!shift_state) { + return; + } + ControllerID controllerID = ControllerID::SHAPE_RELEASE; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->gate_hysteresis_controllable (); + map_encoder (controllerID, control); + } +} + +void +Console1::map_gate_hold () +{ + if (!shift_state) { + return; + } + ControllerID controllerID = ControllerID::SHAPE_SUSTAIN; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->gate_hold_controllable (); + map_encoder (controllerID, control); + } +} + +void +Console1::map_gate_filter_freq () +{ + if (!shift_state) { + return; + } + ControllerID controllerID = ControllerID::SHAPE_PUNCH; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->gate_key_filter_freq_controllable (); + map_encoder (controllerID, control); + } +} + +// EQ Section +void +Console1::map_eq () +{ + if (!_current_stripable) + return; + try { + get_button (EQ).set_led_state (_current_stripable->eq_enable_controllable () + ? _current_stripable->eq_enable_controllable ()->get_value () + : false); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } +} + +void +Console1::map_eq_freq (const uint32_t band) +{ + if (shift_state) { + return; + } + ControllerID controllerID = eq_freq_controller_for_band (band); + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->eq_freq_controllable (band); + map_encoder (controllerID, control); + } +} + +void +Console1::map_eq_gain (const uint32_t band) +{ + if (shift_state) { + return; + } + ControllerID controllerID = eq_gain_controller_for_band (band); + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->eq_gain_controllable (band); + map_encoder (controllerID, control); + } +} + +void +Console1::map_eq_low_shape () +{ + if (!_current_stripable) + return; + try { + uint32_t led_value = _current_stripable->eq_shape_controllable (0) + ? _current_stripable->eq_shape_controllable (0)->get_value () == 0 ? 0 : 63 + : 0; + get_button (ControllerID::LOW_SHAPE).set_led_state (led_value); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } +} + +void +Console1::map_eq_high_shape () +{ + if (!_current_stripable) + return; + try { + uint32_t led_value = _current_stripable->eq_shape_controllable (3) + ? _current_stripable->eq_shape_controllable (3)->get_value () == 0 ? 0 : 63 + : 0; + get_button (ControllerID::HIGH_SHAPE).set_led_state (led_value); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } +} + +// Drive +void +Console1::map_drive () +{ + ControllerID controllerID = ControllerID::CHARACTER; + + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->tape_drive_controllable (); + if (control && _current_stripable->presentation_info ().flags () & PresentationInfo::AudioTrack) { + double val = control->get_value (); + DEBUG_TRACE (DEBUG::Console1, string_compose ("map_drive audio track %1\n", val)); + try { + get_encoder (controllerID).set_value (val == 1 ? 127 : 0); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Encoder not found\n"); + } + } else { + map_encoder (controllerID, control); + } + } +} + +// Sends +void +Console1::map_mb_send_level (const uint32_t n) +{ + uint32_t n_offset = n; +#ifdef MIXBUS + if (_current_stripable->presentation_info ().flags () & PresentationInfo::Flag::Mixbus) { + n_offset = n + 8; + } +#endif + // Theese two sends are available in non-shift state + if (n_offset > 9 && shift_state) { + return; + } else if (n_offset < 10 && !shift_state) // while the rest needs the shift state + { + return; + } + ControllerID controllerID = get_send_controllerid (n_offset); + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->send_level_controllable (n); + map_encoder (controllerID, control); + } +} + +// Comp Section +void +Console1::map_comp () +{ + if (!_current_stripable) + return; + try { + get_button (ControllerID::COMP) + .set_led_state (_current_stripable->comp_enable_controllable () + ? _current_stripable->comp_enable_controllable ()->get_value () + : false); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } +} + +void +Console1::map_comp_mode () +{ + if (!_current_stripable) + return; + try { + double value = _current_stripable->comp_mode_controllable () + ? _current_stripable->comp_mode_controllable ()->get_value () + : false; + DEBUG_TRACE (DEBUG::Console1, string_compose ("****value from comp-type %1\n", value)); + get_mbutton (ControllerID::ORDER).set_led_state (value); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Button not found\n"); + } +} + +void +Console1::map_comp_thresh () +{ + ControllerID controllerID = ControllerID::COMP_THRESH; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->comp_threshold_controllable (); + map_encoder (controllerID, control); + } +} + +void +Console1::map_comp_attack () +{ + ControllerID controllerID = ControllerID::COMP_ATTACK; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->comp_attack_controllable (); + map_encoder (controllerID, control); + } +} + +void +Console1::map_comp_release () +{ + ControllerID controllerID = ControllerID::COMP_RELEASE; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->comp_release_controllable (); + map_encoder (controllerID, control); + } +} + +void +Console1::map_comp_ratio () +{ + ControllerID controllerID = ControllerID::COMP_RATIO; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->comp_ratio_controllable (); + map_encoder (controllerID, control); + } +} + +void +Console1::map_comp_makeup () +{ + ControllerID controllerID = ControllerID::COMP_PAR; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->comp_makeup_controllable (); + map_encoder (controllerID, control); + } +} + +void +Console1::map_comp_emph () +{ + ControllerID controllerID = ControllerID::DRIVE; + if (map_encoder (controllerID)) { + std::shared_ptr control = _current_stripable->comp_key_filter_freq_controllable (); + map_encoder (controllerID, control); + } +} + +bool +Console1::map_encoder (ControllerID controllerID) +{ + if (!_current_stripable) { + try { + get_encoder (controllerID).set_value (0); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Encoder not found\n"); + return false; + } + return false; + } + return true; +} + +void +Console1::map_encoder (ControllerID controllerID, std::shared_ptr control) +{ + + if (!_current_stripable) { + try { + get_encoder (controllerID).set_value (0); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Encoder not found\n"); + } + return; + } + + double val; + double gain; + + if (!control) { + val = 0.0; + } else { + val = control->get_value (); + gain = control_to_midi (control, val); + } + try { + get_encoder (controllerID).set_value (gain); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Encoder not found\n"); + } +} \ No newline at end of file diff --git a/libs/surfaces/console1/console1.cc b/libs/surfaces/console1/console1.cc new file mode 100644 index 0000000000..d33b0bb184 --- /dev/null +++ b/libs/surfaces/console1/console1.cc @@ -0,0 +1,1120 @@ +/* + * 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 "console1.h" + +#include "glibmm-2.4/glibmm/main.h" +#include "pbd/abstract_ui.cc" // instantiate template +#include "pbd/controllable.h" +#include "pbd/i18n.h" + +#include "ardour/audioengine.h" +#include "ardour/debug.h" +#include "ardour/meter.h" +#include "ardour/monitor_control.h" +#include "ardour/phase_control.h" +#include "ardour/readonly_control.h" +#include "ardour/session.h" +#include "ardour/stripable.h" +#include "ardour/track.h" +#include "ardour/vca_manager.h" + +#include "c1_gui.h" +#include "c1_control.h" + +using namespace ARDOUR; +using namespace ArdourSurface; +using namespace PBD; +using namespace Glib; +using namespace std; + +Console1::Console1 (Session& s) + : MIDISurface (s, X_ ("Softube Console1"), X_ ("Console1"), false) + , gui (0) + , blink_state (false) + , rec_enable_state (false) +{ + port_setup (); +} + +Console1::~Console1 () +{ + all_lights_out (); + + MIDISurface::drop (); + + tear_down_gui (); + + /* stop event loop */ + DEBUG_TRACE (DEBUG::Console1, "BaseUI::quit ()\n"); + + BaseUI::quit (); +} + + +void +Console1::all_lights_out () +{ + for (ButtonMap::iterator b = buttons.begin (); b != buttons.end (); ++b) { + b->second.set_led_state (false); + } +} + +int +Console1::set_active (bool yn) +{ + DEBUG_TRACE (DEBUG::Console1, string_compose ("Console1::set_active init with yn: '%1'\n", yn)); + + if (yn == active ()) { + return 0; + } + + if (yn) { + + /* start event loop */ + + DEBUG_TRACE (DEBUG::Console1, "Console1::set_active\n"); + + BaseUI::run (); + + connect_session_signals (); + + } else { + /* Control Protocol Manager never calls us with false, but + * insteads destroys us. + */ + } + + ControlProtocol::set_active (yn); + + DEBUG_TRACE (DEBUG::Console1, string_compose ("Console1::set_active done with yn: '%1'\n", yn)); + + return 0; +} + +std::string +Console1::input_port_name () const +{ +#ifdef __APPLE__ + /* the origin of the numeric magic identifiers is known only to Ableton + and may change in time. This is part of how CoreMIDI works. + */ + return X_ ("system:midi_capture_2849385499"); +#else + return X_ ("Console1 Recv"); +#endif +} + +std::string +Console1::output_port_name () const +{ +#ifdef __APPLE__ + /* the origin of the numeric magic identifiers is known only to Ableton + and may change in time. This is part of how CoreMIDI works. + */ + return X_ ("system:midi_playback_1721623007"); +#else + return X_ ("Console1 Send"); +#endif +} + +int +Console1::begin_using_device () +{ + DEBUG_TRACE (DEBUG::Console1, "sending device inquiry message...\n"); + + if (MIDISurface::begin_using_device ()) { + return -1; + } + /* + with this sysex command we can enter the 'native mode' + But there's no need to do so + f0 7d 20 00 00 00 01 00 7f 49 6f 6c 73 00 f7 + */ + + setup_controls (); + + /* + Connection to the blink-timer + */ + Glib::RefPtr blink_timeout = Glib::TimeoutSource::create (200); // milliseconds + blink_connection = blink_timeout->connect (sigc::mem_fun (*this, &Console1::blinker)); + blink_timeout->attach (main_loop ()->get_context ()); + + /* Connection to the peridic timer for meters */ + Glib::RefPtr periodic_timer = Glib::TimeoutSource::create (100); + periodic_connection = periodic_timer->connect (sigc::mem_fun (*this, &Console1::periodic)); + periodic_timer->attach (main_loop ()->get_context ()); + + DEBUG_TRACE (DEBUG::Console1, "************** begin_using_device() ********************\n"); + create_strip_invetory (); + connect_internal_signals (); + map_shift (false); + if (!first_selected_stripable ()) { + select_rid_by_index (0); + } + stripable_selection_changed (); + return 0; +} + +void +Console1::connect_session_signals () +{ + DEBUG_TRACE (DEBUG::Console1, "connect_session_signals\n"); + // receive routes added + session->RouteAdded.connect ( + session_connections, MISSING_INVALIDATOR, boost::bind (&Console1::create_strip_invetory, this), this); + // receive VCAs added + session->vca_manager ().VCAAdded.connect ( + session_connections, MISSING_INVALIDATOR, boost::bind (&Console1::create_strip_invetory, this), this); + + // receive record state toggled + // session->RecordStateChanged.connect(session_connections, + // MISSING_INVALIDATOR, boost::bind + // (&MIDISurface::notify_record_state_changed, this), this); receive + // transport + session->TransportStateChange.connect ( + session_connections, MISSING_INVALIDATOR, boost::bind (&Console1::notify_transport_state_changed, this), this); + // session->TransportLooped.connect (session_connections, + // MISSING_INVALIDATOR, boost::bind + // (&MIDISurface::notify_loop_state_changed, this), this); receive punch-in + // and punch-out + Config->ParameterChanged.connect ( + session_connections, MISSING_INVALIDATOR, boost::bind (&Console1::notify_parameter_changed, this, _1), this); + session->config.ParameterChanged.connect ( + session_connections, MISSING_INVALIDATOR, boost::bind (&Console1::notify_parameter_changed, this, _1), this); + // receive rude solo changed + session->SoloActive.connect ( + session_connections, MISSING_INVALIDATOR, boost::bind (&Console1::notify_solo_active_changed, this, _1), this); + // window.signal_window_state_event().connect (sigc::bind (sigc::mem_fun (*this, + // &ARDOUR_UI::tabbed_window_state_event_handler), owner)); +} + +void +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); + GotoView.connect ( + console1_connections, + MISSING_INVALIDATOR, + [] (uint val) { DEBUG_TRACE (DEBUG::Console1, string_compose ("GotooView: %1\n", val)); }, + this); + VerticalZoomInSelected.connect ( + console1_connections, MISSING_INVALIDATOR, [] () { DEBUG_TRACE (DEBUG::Console1, "VerticalZoomIn\n"); }, this); + VerticalZoomOutSelected.connect ( + console1_connections, MISSING_INVALIDATOR, [] () { DEBUG_TRACE (DEBUG::Console1, "VerticalZoomOut\n"); }, this); +} + +void +Console1::setup_controls () +{ + + for (uint32_t i = 0; i < 20; ++i) { + ControllerButton track_select_button ( + *this, + ControllerID (FOCUS1 + i), + boost::function (boost::bind (&Console1::select, this, i))); + } + + ControllerButton shift_button ( + *this, ControllerID::PRESET, boost::function (boost::bind (&Console1::shift, this, _1))); + + ControllerButton rude_solo ( + *this, ControllerID::DISPLAY_ON, boost::function (boost::bind (&Console1::rude_solo, this, _1))); + ControllerButton zoom_button ( + *this, ControllerID::MODE, boost::function (boost::bind (&Console1::zoom, this, _1))); + MultiStateButton view (*this, + ControllerID::EXTERNAL_SIDECHAIN, + std::vector{ 0, 63, 127 }, + boost::function (boost::bind (&Console1::window, this, _1))); + + ControllerButton bank_up_button ( + *this, ControllerID::PAGE_UP, boost::function (boost::bind (&Console1::bank, this, true))); + ControllerButton bank_down_button ( + *this, ControllerID::PAGE_DOWN, boost::function (boost::bind (&Console1::bank, this, false))); + + ControllerButton mute_button ( + *this, ControllerID::MUTE, boost::function (boost::bind (&Console1::mute, this, _1))); + ControllerButton solo_button ( + *this, ControllerID::SOLO, boost::function (boost::bind (&Console1::solo, this, _1))); + ControllerButton phase_button ( + *this, ControllerID::PHASE_INV, boost::function (boost::bind (&Console1::phase, this, _1))); + + /* + Console 1: Input Gain - Ardour / Mixbus: Trim + */ + Encoder trim_encoder ( + *this, ControllerID::GAIN, boost::function (boost::bind (&Console1::trim, this, _1))); + + /* + Console 1: Volume - Ardour / Mixbus: Gain + */ + Encoder gain_encoder ( + *this, ControllerID::VOLUME, boost::function (boost::bind (&Console1::gain, this, _1))); + + Encoder pan_encoder ( + *this, ControllerID::PAN, boost::function (boost::bind (&Console1::pan, this, _1))); + + /* Filter Section*/ + ControllerButton filter_button (*this, + ControllerID::FILTER_TO_COMPRESSORS, + boost::function (boost::bind (&Console1::filter, this, _1))); + Encoder low_cut_encoder ( + *this, ControllerID::LOW_CUT, boost::function (boost::bind (&Console1::low_cut, this, _1))); + Encoder high_cut_encoder ( + *this, ControllerID::HIGH_CUT, boost::function (boost::bind (&Console1::high_cut, this, _1))); + + /* Gate Section */ + ControllerButton gate_on_off ( + *this, ControllerID::SHAPE, boost::function (boost::bind (&Console1::gate, this, _1))); + ControllerButton gate_scf_listen ( + *this, + ControllerID::HARD_GATE, + boost::function (boost::bind (&Console1::gate_scf, this, _1)), + boost::function (boost::bind (&Console1::gate_listen, this, _1))); + Encoder gate_thresh_encoder (*this, + ControllerID::SHAPE_GATE, + boost::function (boost::bind (&Console1::gate_thresh, this, _1))); + Encoder gate_release_encoder (*this, + ControllerID::SHAPE_RELEASE, + boost::function (boost::bind (&Console1::gate_release, this, _1)), + boost::function (boost::bind (&Console1::gate_hyst, this, _1))); + Encoder gate_attack_encoder (*this, + ControllerID::SHAPE_SUSTAIN, + boost::function (boost::bind (&Console1::gate_attack, this, _1)), + boost::function (boost::bind (&Console1::gate_hold, this, _1))); + Encoder gate_depth_encoder (*this, + ControllerID::SHAPE_PUNCH, + boost::function (boost::bind (&Console1::gate_depth, this, _1)), + boost::function (boost::bind (&Console1::gate_filter_freq, this, _1))); + + Meter gate_meter (*this, ControllerID::SHAPE_METER, boost::function ([] () {})); + + /* EQ Section */ + ControllerButton eq_on_off ( + *this, ControllerID::EQ, boost::function (boost::bind (&Console1::eq, this, _1))); + + for (uint32_t i = 0; i < 4; ++i) { + Encoder low_freq_encoder ( + *this, + eq_freq_controller_for_band (i), + boost::function (boost::bind (&Console1::eq_freq, this, i, _1)), + boost::function (boost::bind (&Console1::mb_send_level, this, i, _1))); + Encoder low_gain_encoder ( + *this, + eq_gain_controller_for_band (i), + boost::function (boost::bind (&Console1::eq_gain, this, i, _1)), + boost::function (boost::bind (&Console1::mb_send_level, this, i + 4, _1))); + } + Encoder low_mid_shape_encoder ( + *this, + ControllerID::LOW_MID_SHAPE, + boost::function (boost::bind (&Console1::mb_send_level, this, 10, _1)), + boost::function (boost::bind (&Console1::mb_send_level, this, 8, _1))); + Encoder high_mid_shape_encoder ( + *this, + ControllerID::HIGH_MID_SHAPE, + boost::function (boost::bind (&Console1::mb_send_level, this, 11, _1)), + boost::function (boost::bind (&Console1::mb_send_level, this, 9, _1))); + + ControllerButton eq_low_shape (*this, + ControllerID::LOW_SHAPE, + boost::function (boost::bind (&Console1::eq_low_shape, this, _1))); + ControllerButton eq_high_shape ( + *this, + ControllerID::HIGH_SHAPE, + boost::function (boost::bind (&Console1::eq_high_shape, this, _1))); + + Encoder drive_encoder ( + *this, ControllerID::CHARACTER, boost::function (boost::bind (&Console1::drive, this, _1))); + + /* Compressor Section */ + ControllerButton comp_on_off ( + *this, ControllerID::COMP, boost::function (boost::bind (&Console1::comp, this, _1))); + MultiStateButton comp_mode (*this, + ControllerID::ORDER, + std::vector{ 0, 63, 127 }, + boost::function (boost::bind (&Console1::comp_mode, this, _1))); + + Encoder comp_thresh_encoder (*this, + ControllerID::COMP_THRESH, + boost::function (boost::bind (&Console1::comp_thresh, this, _1))); + Encoder comp_attack_encoder (*this, + ControllerID::COMP_ATTACK, + boost::function (boost::bind (&Console1::comp_attack, this, _1))); + Encoder comp_release_encoder (*this, + ControllerID::COMP_RELEASE, + boost::function (boost::bind (&Console1::comp_release, this, _1))); + Encoder comp_ratio_encoder (*this, + ControllerID::COMP_RATIO, + boost::function (boost::bind (&Console1::comp_ratio, this, _1))); + Encoder comp_makeup_encoder ( + *this, ControllerID::COMP_PAR, boost::function (boost::bind (&Console1::comp_makeup, this, _1))); + Encoder comp_emph_encoder ( + *this, ControllerID::DRIVE, boost::function (boost::bind (&Console1::comp_emph, this, _1))); + + Meter compressor_meter (*this, ControllerID::COMP_METER, boost::function ([] () {})); + + /* Output Section */ + Meter output_meter_l (*this, ControllerID::OUTPUT_METER_L, boost::function ([] () {})); + Meter output_meter_r (*this, ControllerID::OUTPUT_METER_R, boost::function ([] () {})); +} + +int +Console1::stop_using_device () +{ + DEBUG_TRACE (DEBUG::Console1, "stop_using_device()\n"); + + blink_connection.disconnect (); + periodic_connection.disconnect (); + stripable_connections.drop_connections (); + return 0; +} + +void +Console1::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* tb) +{ + uint32_t controller_number = static_cast (tb->controller_number); + uint32_t value = static_cast (tb->value); + + DEBUG_TRACE (DEBUG::Console1, + string_compose ("handle_midi_controller_message cn: '%1' val: '%2'\n", controller_number, value)); + try { + Encoder e = get_encoder (ControllerID (controller_number)); + if (shift_state && e.shift_action) { + e.shift_action (value); + } else { + e.action (value); + } + return; + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, + string_compose ("handle_midi_controller_message: encoder not found cn: " + "'%1' val: '%2'\n", + controller_number, + value)); + } + + try { + ControllerButton& b = get_button (ControllerID (controller_number)); + if (shift_state && b.shift_action) { + DEBUG_TRACE (DEBUG::Console1, "Executing shift_action\n"); + b.shift_action (value); + } else { + DEBUG_TRACE (DEBUG::Console1, "Executing action\n"); + b.action (value); + } + return; + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, + string_compose ("handle_midi_controller_message: button not found cn: " + "'%1' val: '%2'\n", + controller_number, + value)); + } + + try { + MultiStateButton mb = get_mbutton (ControllerID (controller_number)); + if (shift_state && mb.shift_action) { + mb.shift_action (value); + } else { + mb.action (value); + } + + return; + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, + string_compose ("handle_midi_controller_message: mbutton not found cn: " + "'%1' val: '%2'\n", + controller_number, + value)); + } +} + +void +Console1::tabbed_window_state_event_handler (GdkEventWindowState* ev, void* object) +{ + DEBUG_TRACE (DEBUG::Console1, string_compose ("tabbed_window_state_event_handler: %1\n", ev->type)); +} + +void +Console1::notify_solo_active_changed (bool state) +{ + DEBUG_TRACE (DEBUG::Console1, "notify_active_solo_changed() \n"); + try { + get_button (ControllerID::DISPLAY_ON).set_led_value (state ? 127 : 0); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "button not found"); + } +} + +void +Console1::notify_parameter_changed (std::string s) +{ + DEBUG_TRACE (DEBUG::Console1, string_compose ("notify_parameter_changed: %1\n", s)); +} + +void +Console1::notify_transport_state_changed () +{ + DEBUG_TRACE (DEBUG::Console1, "transport_state_changed() \n"); + rolling = session->transport_state_rolling (); +} + +void +Console1::stripable_selection_changed () +{ + DEBUG_TRACE (DEBUG::Console1, "stripable_selection_changed \n"); + set_current_stripable (first_selected_stripable ()); +} + +void +Console1::drop_current_stripable () +{ + if (_current_stripable) { + if (_current_stripable == session->monitor_out ()) { + set_current_stripable (session->master_out ()); + } else { + set_current_stripable (std::shared_ptr ()); + } + } +} + +void +Console1::set_current_stripable (std::shared_ptr r) +{ + DEBUG_TRACE (DEBUG::Console1, "set_current_stripable \n"); + stripable_connections.drop_connections (); + + _current_stripable = r; + + if (_current_stripable) { + DEBUG_TRACE (DEBUG::Console1, "current_stripable found: \n"); + + PresentationInfo pi = _current_stripable->presentation_info (); + + DEBUG_TRACE (DEBUG::Console1, string_compose ("current_stripable %1 - %2\n", pi.order (), pi.flags ())); + + gate_redux_meter = _current_stripable->gate_redux_controllable (); + comp_redux_meter = _current_stripable->comp_redux_controllable (); + + /* + Support all types of pan controls / find first available control + */ + if (_current_stripable->pan_azimuth_control ()) + current_pan_control = _current_stripable->pan_azimuth_control (); + else if (_current_stripable->pan_elevation_control ()) + current_pan_control = _current_stripable->pan_azimuth_control (); + else if (_current_stripable->pan_width_control ()) + current_pan_control = _current_stripable->pan_width_control (); + else if (_current_stripable->pan_frontback_control ()) + current_pan_control = _current_stripable->pan_frontback_control (); + else if (_current_stripable->pan_lfe_control ()) + current_pan_control = _current_stripable->pan_lfe_control (); + else + current_pan_control = nullptr; + + std::shared_ptr pan_control = current_pan_control; + if (pan_control) + pan_control->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_pan, this), this); + + _current_stripable->DropReferences.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::drop_current_stripable, this), this); + + _current_stripable->mute_control ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_mute, this), this); + + _current_stripable->solo_control ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_solo, this), this); + + _current_stripable->phase_control ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_phase, this), this); + + // Rec Enabled + std::shared_ptr t = std::dynamic_pointer_cast (_current_stripable); + if (t) { + t->rec_enable_control ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_recenable, this), this); + } + + // Monitor + if (_current_stripable->monitoring_control ()) { + _current_stripable->monitoring_control ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_monitoring, this), this); + } + + // Trim + std::shared_ptr trim_control = _current_stripable->trim_control (); + if (trim_control) { + trim_control->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_trim, this), this); + } + // Gain + std::shared_ptr gain_control = _current_stripable->gain_control (); + if (gain_control) { + gain_control->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_gain, this), this); + + // control->alist()->automation_state_changed.connect + // (stripable_connections, MISSING_INVALIDATOR, boost::bind + // (&Console1::map_auto, this), this); + } + + // Filter Section + if (_current_stripable->filter_enable_controllable (true)) { + _current_stripable->filter_enable_controllable (true)->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_filter, this), this); + } + + if (_current_stripable->filter_freq_controllable (true)) { + _current_stripable->filter_freq_controllable (true)->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_low_cut, this), this); + } + + if (_current_stripable->filter_freq_controllable (false)) { + _current_stripable->filter_freq_controllable (false)->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_high_cut, this), this); + } + + // Gate Section + if (_current_stripable->gate_enable_controllable ()) { + _current_stripable->gate_enable_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_gate, this), this); + } + + if (_current_stripable->gate_key_filter_enable_controllable ()) { + _current_stripable->gate_key_filter_enable_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_gate_scf, this), this); + } + + if (_current_stripable->gate_key_listen_controllable ()) { + _current_stripable->gate_key_listen_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_gate_listen, this), this); + } + + if (_current_stripable->gate_threshold_controllable ()) { + _current_stripable->gate_threshold_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_gate_thresh, this), this); + } + + if (_current_stripable->gate_depth_controllable ()) { + _current_stripable->gate_depth_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_gate_depth, this), this); + } + + if (_current_stripable->gate_release_controllable ()) { + _current_stripable->gate_release_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_gate_release, this), this); + } + + if (_current_stripable->gate_attack_controllable ()) { + _current_stripable->gate_attack_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_gate_attack, this), this); + } + + if (_current_stripable->gate_hysteresis_controllable ()) { + _current_stripable->gate_hysteresis_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_gate_hyst, this), this); + } + + if (_current_stripable->gate_hold_controllable ()) { + _current_stripable->gate_hold_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_gate_hold, this), this); + } + + if (_current_stripable->gate_key_filter_freq_controllable ()) { + _current_stripable->gate_key_filter_freq_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_gate_filter_freq, this), this); + } + + // EQ Section + if (_current_stripable->eq_enable_controllable ()) { + _current_stripable->eq_enable_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_eq, this), this); + } + + for (uint32_t i = 0; i < _current_stripable->eq_band_cnt (); ++i) { + if (_current_stripable->eq_freq_controllable (i)) { + _current_stripable->eq_freq_controllable (i)->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_eq_freq, this, i), this); + } + if (_current_stripable->eq_gain_controllable (i)) { + _current_stripable->eq_gain_controllable (i)->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_eq_gain, this, i), this); + } + } + + if (_current_stripable->eq_shape_controllable (0)) { + _current_stripable->eq_shape_controllable (0)->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_eq_low_shape, this), this); + } + + if (_current_stripable->eq_shape_controllable (3)) { + _current_stripable->eq_shape_controllable (3)->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_eq_high_shape, this), this); + } + + // Drive + if (_current_stripable->tape_drive_controllable ()) { + _current_stripable->tape_drive_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_drive, this), this); + } + + // Mixbus Sends + for (uint32_t i = 0; i < 12; ++i) { + if (_current_stripable->send_level_controllable (i)) { + _current_stripable->send_level_controllable (i)->Changed.connect ( + stripable_connections, + MISSING_INVALIDATOR, + boost::bind (&Console1::map_mb_send_level, this, i), + this); + } + } + + // Comp Section + if (_current_stripable->comp_enable_controllable ()) { + _current_stripable->comp_enable_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_comp, this), this); + } + + if (_current_stripable->comp_mode_controllable ()) { + _current_stripable->comp_mode_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_comp_mode, this), this); + } + + if (_current_stripable->comp_threshold_controllable ()) { + _current_stripable->comp_threshold_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_comp_thresh, this), this); + } + + if (_current_stripable->comp_attack_controllable ()) { + _current_stripable->comp_attack_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_comp_attack, this), this); + } + + if (_current_stripable->comp_release_controllable ()) { + _current_stripable->comp_release_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_comp_release, this), this); + } + + if (_current_stripable->comp_ratio_controllable ()) { + _current_stripable->comp_ratio_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_comp_ratio, this), this); + } + + if (_current_stripable->comp_makeup_controllable ()) { + _current_stripable->comp_makeup_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_comp_makeup, this), this); + } + + if (_current_stripable->comp_key_filter_freq_controllable ()) { + _current_stripable->comp_key_filter_freq_controllable ()->Changed.connect ( + stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_comp_emph, this), this); + } + + uint32_t index = get_index_by_inventory_order (pi.order ()); + current_strippable_index = index % bank_size; + uint32_t bank = index / bank_size; + if (bank != current_bank) { + current_bank = bank; + BankChange (); + } + DEBUG_TRACE ( + DEBUG::Console1, + string_compose ( + "current_stripable: rid %1, bank %2, index %3 \n", index, current_bank, current_strippable_index)); + + } else { + gate_redux_meter = 0; + comp_redux_meter = 0; + } + + // ToDo: subscribe to the fader automation modes so we can light the LEDs + + map_stripable_state (); +} + +void +Console1::map_stripable_state () +{ + if (!_current_stripable) { + stop_blinking (MUTE); + stop_blinking (SOLO); + stop_blinking (PHASE_INV); + } else { + map_select (); + + map_bank (); + map_gain (); + map_pan (); + map_phase (); + map_recenable (); + map_solo (); + map_trim (); + + // Filter Section + map_filter (); + map_low_cut (); + map_high_cut (); + + // Gate Section + map_gate (); + map_gate_scf (); + map_gate_listen (); + map_gate_thresh (); + map_gate_attack (); + map_gate_release (); + map_gate_depth (); + map_gate_hyst (); + map_gate_hold (); + map_gate_filter_freq (); + + // EQ Section + map_eq (); + for (uint32_t i = 0; i < _current_stripable->eq_band_cnt (); ++i) { + map_eq_freq (i); + map_eq_gain (i); + } + map_eq_low_shape (); + map_eq_high_shape (); + + for (int i = 0; i < 12; ++i) { + map_mb_send_level (i); + } + + // Drive + map_drive (); + + // Comp Section + map_comp (); + map_comp_mode (); + map_comp_thresh (); + map_comp_attack (); + map_comp_release (); + map_comp_ratio (); + map_comp_makeup (); + map_comp_emph (); + + if (_current_stripable == session->monitor_out ()) { + // map_cut(); + } else { + map_mute (); + } + } +} + +void +Console1::stop_blinking (ControllerID id) +{ + blinkers.remove (id); + get_button (id).set_led_state (false); +} + +void +Console1::start_blinking (ControllerID id) +{ + blinkers.push_back (id); + get_button (id).set_led_state (true); +} + +bool +Console1::blinker () +{ + blink_state = !blink_state; + + for (Blinkers::iterator b = blinkers.begin (); b != blinkers.end (); b++) { + try { + get_button (*b).set_led_state (blink_state); + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Blinking Button not found ...\n"); + } + } + + return true; +} + +ControllerButton& +Console1::get_button (ControllerID id) const +{ + ButtonMap::const_iterator b = buttons.find (id); + if (b == buttons.end ()) + throw (ControlNotFoundException ()); + return const_cast (b->second); +} + +Meter& +Console1::get_meter (ControllerID id) const +{ + MeterMap::const_iterator m = meters.find (id); + if (m == meters.end ()) + throw (ControlNotFoundException ()); + return const_cast (m->second); +} + +Encoder& +Console1::get_encoder (ControllerID id) const +{ + EncoderMap::const_iterator m = encoders.find (id); + if (m == encoders.end ()) + throw (ControlNotFoundException ()); + return const_cast (m->second); +} + +MultiStateButton& +Console1::get_mbutton (ControllerID id) const +{ + MultiStateButtonMap::const_iterator m = multi_buttons.find (id); + if (m == multi_buttons.end ()) + throw (ControlNotFoundException ()); + return const_cast (m->second); +} + +ControllerID +Console1::get_send_controllerid (uint32_t n) +{ + SendControllerMap::const_iterator s = send_controllers.find (n); + if (s != send_controllers.end ()) + return s->second; + else + return CONTROLLER_NONE; +} + +bool +Console1::periodic () +{ + periodic_update_meter (); + return true; +} + +void +Console1::periodic_update_meter () +{ + if (_current_stripable) { + bool show = (rolling || !strip_recenabled || (monitor_state & ARDOUR::MonitorState::MonitoringInput)); + if (_current_stripable->peak_meter ()) { + uint32_t val_l, val_r; + if (!show) { + val_l = val_r = 0; + } else { + uint32_t chan_count = _current_stripable->peak_meter ()->input_streams ().n_total (); + float dB = _current_stripable->peak_meter ()->meter_level (0, MeterMCP); + val_l = val_r = calculate_meter (dB); + if (chan_count > 1) { + dB = _current_stripable->peak_meter ()->meter_level (1, MeterMCP); + val_r = calculate_meter (dB); + } + } + try { + if (val_l != last_output_meter_l) { + get_meter (OUTPUT_METER_L).set_value (val_l); + last_output_meter_l = val_l; + } + if (val_r != last_output_meter_r) { + get_meter (OUTPUT_METER_R).set_value (val_r); + last_output_meter_r = val_r; + } + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Meter not found ...\n"); + } + } + if (gate_redux_meter) { + uint32_t val; + if (!show) { + val = 127; + } else { + float dB = gate_redux_meter->get_parameter (); + val = 127 * dB; + } + try { + if (val != last_gate_meter) { + get_meter (SHAPE_METER).set_value (val); + last_gate_meter = val; + } + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Meter not found ...\n"); + } + } + if (comp_redux_meter) { + uint32_t val; + if (!show) { + val = 127; + } else { + float rx = comp_redux_meter->get_parameter () * 127.f; + val = pow (3.3 + 0.11 * rx, 4); + val = std::min (127.f, std::max (0.f, rx)); + } + try { + if (val != last_comp_redux) { + last_comp_redux = val; + val = val * 0.6 + last_comp_redux * 0.4; + get_meter (COMP_METER).set_value (val); + } + } catch (ControlNotFoundException& e) { + DEBUG_TRACE (DEBUG::Console1, "Meter not found ...\n"); + } + } + } +} + +float +Console1::calculate_meter (float dB) +{ + return pow ((8.7 + 0.18 * dB), 2.1); +} + +uint32_t +Console1::control_to_midi (Controllable controllable, float val, uint32_t max_value_for_type) +{ + if (!controllable) { + return 0; + } + + if (controllable->is_gain_like ()) { + return controllable->internal_to_interface (val) * max_value_for_type; + } + + float control_min = controllable->lower (); + float control_max = controllable->upper (); + float control_range = control_max - control_min; + + if (controllable->is_toggle ()) { + if (val >= (control_min + (control_range / 2.0f))) { + return max_value_for_type; + } else { + return 0; + } + } else { + std::shared_ptr actl = std::dynamic_pointer_cast (controllable); + if (actl) { + control_min = actl->internal_to_interface (control_min); + control_max = actl->internal_to_interface (control_max); + control_range = control_max - control_min; + val = actl->internal_to_interface (val); + } + } + // fiddle value of max so value doesn't jump from 125 to 127 for 1.0 + // otherwise decrement won't work. + return (val - control_min) / control_range * (max_value_for_type - 1); +} + +float +Console1::midi_to_control (Controllable controllable, uint32_t val, uint32_t max_value_for_type) +{ + if (!controllable) { + return 0; + } + /* fiddle with MIDI value so that we get an odd number of integer steps + and can thus represent "middle" precisely as 0.5. this maps to + the range 0..+1.0 (0 to 126) + */ + + float fv = (val == 0 ? 0 : float (val - 1) / (max_value_for_type - 1)); + + if (controllable->is_gain_like ()) { + return controllable->interface_to_internal (fv); + } + DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Raw value %1 float %2\n", val, fv)); + + float control_min = controllable->lower (); + float control_max = controllable->upper (); + float control_range = control_max - control_min; + DEBUG_TRACE (DEBUG::GenericMidi, + string_compose ("Min %1 Max %2 Range %3\n", control_min, control_max, control_range)); + + std::shared_ptr actl = std::dynamic_pointer_cast (controllable); + if (actl) { + if (fv == 0.f) + return control_min; + if (fv == 1.f) + return control_max; + control_min = actl->internal_to_interface (control_min); + control_max = actl->internal_to_interface (control_max); + control_range = control_max - control_min; + return actl->interface_to_internal ((fv * control_range) + control_min); + } + return (fv * control_range) + control_min; +} + +void +Console1::create_strip_invetory () +{ + DEBUG_TRACE (DEBUG::Console1, "create_strip_invetory()\n"); + StripableList sl; + boost::optional master_order; + strip_inventory.clear (); + session->get_stripables (sl); + uint32_t index = 0; + for (const auto& s : sl) { + PresentationInfo pi = s->presentation_info (); + if (pi.flags () & ARDOUR::PresentationInfo::Hidden) { + DEBUG_TRACE (DEBUG::Console1, string_compose ("strip hidden: index %1, order %2\n", index, pi.order ())); + continue; + } + if (pi.flags () & ARDOUR::PresentationInfo::MasterOut) { + master_order = pi.order (); + DEBUG_TRACE (DEBUG::Console1, + string_compose ("master strip found at index %1, order %2\n", index, pi.order ())); + continue; + } + if (pi.flags () & ARDOUR::PresentationInfo::MonitorOut) { + DEBUG_TRACE (DEBUG::Console1, + string_compose ("monitor strip found at index %1, order %2\n", index, pi.order ())); + continue; + } + strip_inventory.insert (std::make_pair (index, pi.order ())); + DEBUG_TRACE (DEBUG::Console1, string_compose ("insert strip at index %1, order %2\n", index, pi.order ())); + ++index; + } + if (master_order.has_value ()) { + strip_inventory.insert (std::make_pair (index, master_order.value ())); + } + DEBUG_TRACE (DEBUG::Console1, + string_compose ("create_strip_invetory - inventory size %1\n", strip_inventory.size ())); +} + +order_t +Console1::get_inventory_order_by_index (uint32_t index) +{ + StripInventoryMap::const_iterator s = strip_inventory.find (index); + if (s == strip_inventory.end ()) + throw (ControlNotFoundException ()); + return s->second; +} + +uint32_t +Console1::get_index_by_inventory_order (order_t order) +{ + for (std::pair i : strip_inventory) { + if (i.second == order) { + return i.first; + } + } + return 0; +} + +void +Console1::select_rid_by_index (uint32_t index) +{ +#ifdef MIXBUS + set_rid_selection (index + 1); +#else + set_rid_selection (index + 2); +#endif +} diff --git a/libs/surfaces/console1/console1.h b/libs/surfaces/console1/console1.h new file mode 100644 index 0000000000..25198f1203 --- /dev/null +++ b/libs/surfaces/console1/console1.h @@ -0,0 +1,468 @@ +/* + * 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. + */ + +#ifndef ardour_surface_console1_h +#define ardour_surface_console1_h + +#include +#include +#include + +#include + +#define ABSTRACT_UI_EXPORTS +#include + +#include "glibmm/main.h" +#include "pbd/abstract_ui.h" +#include "pbd/controllable.h" + +#include "ardour/presentation_info.h" +#include "ardour/readonly_control.h" +#include "ardour/types.h" +#include "control_protocol/control_protocol.h" +#include "gdk/gdkevents.h" +#include "midi_surface/midi_byte_array.h" +#include "midi_surface/midi_surface.h" + +namespace MIDI { +class Parser; +class Port; +} + +namespace ARDOUR { +class AsyncMIDIPort; +class Bundle; +class Port; +class Session; +class MidiPort; +} + +namespace PBD { +class Controllable; +} + +class MIDIControllable; +class MIDIFunction; +class MIDIAction; + +namespace ArdourSurface { + +class C1GUI; + +class Controller; +class ControllerButton; +class MultiStateButton; +class Meter; +class Encoder; + +using Controllable = std::shared_ptr; +using order_t = ARDOUR::PresentationInfo::order_t; + +class ControlNotFoundException : public std::exception +{ + public: + ControlNotFoundException () {} + virtual ~ControlNotFoundException () {} +}; + +class Console1 : public MIDISurface +{ + + friend Controller; + friend ControllerButton; + friend MultiStateButton; + friend Meter; + friend Encoder; + + public: + Console1 (ARDOUR::Session&); + virtual ~Console1 (); + + int set_active (bool yn); + + bool has_editor () const { return true; } + void* get_gui () const; + void tear_down_gui () override; + + std::string input_port_name () const override; + std::string output_port_name () const override; + + /*XMLNode& get_state () const; + int set_state (const XMLNode&, int version);*/ + PBD::Signal0 ConnectionChange; + + /* Timer Events */ + PBD::Signal1 BlinkIt; + PBD::Signal0 Periodic; + + /* Local Signals */ + PBD::Signal0 BankChange; + PBD::Signal1 ShiftChange; + + + enum ControllerID + { + CONTROLLER_NONE = 0, + VOLUME = 7, + PAN = 10, + MUTE = 12, + SOLO = 13, + ORDER = 14, + DRIVE = 15, + EXTERNAL_SIDECHAIN = 17, + CHARACTER = 18, + FOCUS1 = 21, + FOCUS2, + FOCUS3, + FOCUS4, + FOCUS5, + FOCUS6, + FOCUS7, + FOCUS8, + FOCUS9, + FOCUS10, + FOCUS11, + FOCUS12, + FOCUS13, + FOCUS14, + FOCUS15, + FOCUS16, + FOCUS17, + FOCUS18, + FOCUS19, + FOCUS20 = 40, + COMP = 46, + COMP_THRESH = 47, + COMP_RELEASE = 48, + COMP_RATIO = 49, + COMP_PAR = 50, + COMP_ATTACK = 51, + SHAPE = 53, + SHAPE_GATE = 54, + SHAPE_SUSTAIN = 55, + SHAPE_RELEASE = 56, + SHAPE_PUNCH = 57, + PRESET = 58, + HARD_GATE = 59, + FILTER_TO_COMPRESSORS = 61, + HIGH_SHAPE = 65, + EQ = 80, + HIGH_GAIN = 82, + HIGH_FREQ = 83, + HIGH_MID_GAIN = 85, + HIGH_MID_FREQ = 86, + HIGH_MID_SHAPE = 87, + LOW_MID_GAIN = 88, + LOW_MID_FREQ = 89, + LOW_MID_SHAPE = 90, + LOW_GAIN = 91, + LOW_FREQ = 92, + LOW_SHAPE = 93, + PAGE_UP = 96, + PAGE_DOWN = 97, + DISPLAY_ON = 102, + LOW_CUT = 103, + MODE = 104, + HIGH_CUT = 105, + GAIN = 107, + PHASE_INV = 108, + INPUT_METER_L = 110, + INPUT_METER_R = 111, + OUTPUT_METER_L = 112, + OUTPUT_METER_R = 113, + SHAPE_METER = 114, + COMP_METER = 115, + TRACK_COPY = 120, + TRACK_GROUP = 123, + + }; + + private: + /* GUI */ + mutable C1GUI* gui; + void build_gui (); + + /* Configuration */ + const uint32_t bank_size = 20; + + bool shift_state = false; + + bool rolling = false; + uint32_t current_bank = 0; + uint32_t current_strippable_index = 0; + + std::shared_ptr current_pan_control = nullptr; + + std::shared_ptr _current_stripable; + std::weak_ptr pre_master_stripable; + std::weak_ptr pre_monitor_stripable; + + void setup_controls (); + + bool strip_recenabled = false; + ARDOUR::MonitorState monitor_state = ARDOUR::MonitorState::MonitoringSilence; + + int begin_using_device (); + int stop_using_device (); + + int device_acquire () { return 0; } + void device_release () {} + + void connect_session_signals (); + void connect_internal_signals (); + + /* MIDI-Message handler - we only have controller messages */ + void handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* tb); + + void tabbed_window_state_event_handler (GdkEventWindowState* ev, void* object); + + /* Strip inventory */ + typedef std::map StripInventoryMap; + + StripInventoryMap strip_inventory; + + void create_strip_invetory (); + + order_t get_inventory_order_by_index (const uint32_t index); + uint32_t get_index_by_inventory_order (order_t order); + + void select_rid_by_index (const uint32_t index); + + /* Controller Maps*/ + typedef std::map ButtonMap; + typedef std::map MultiStateButtonMap; + typedef std::map MeterMap; + typedef std::map EncoderMap; + + ButtonMap buttons; + ControllerButton& get_button (ControllerID) const; + + MultiStateButtonMap multi_buttons; + MultiStateButton& get_mbutton (ControllerID id) const; + + MeterMap meters; + Meter& get_meter (ControllerID) const; + + EncoderMap encoders; + Encoder& get_encoder (ControllerID) const; + + typedef std::map SendControllerMap; + SendControllerMap send_controllers{ { 0, LOW_FREQ }, { 1, LOW_MID_FREQ }, { 2, HIGH_MID_FREQ }, + { 3, HIGH_FREQ }, { 4, LOW_GAIN }, { 5, LOW_MID_GAIN }, + { 6, HIGH_MID_GAIN }, { 7, HIGH_GAIN }, { 8, LOW_MID_SHAPE }, + { 9, HIGH_MID_SHAPE }, { 10, LOW_MID_SHAPE }, { 11, HIGH_MID_SHAPE } }; + + ControllerID get_send_controllerid (uint32_t); + + /* */ + void all_lights_out (); + + void notify_transport_state_changed () override; + void notify_solo_active_changed (bool) override; + + sigc::connection periodic_connection; + + bool periodic (); + void periodic_update_meter (); + + // Meter Handlig + uint32_t last_output_meter_l = 0; + uint32_t last_output_meter_r = 0; + + std::shared_ptr gate_redux_meter = 0; + uint32_t last_gate_meter = 0; + + std::shared_ptr comp_redux_meter = 0; + uint32_t last_comp_redux = 0; + + sigc::connection blink_connection; + typedef std::list Blinkers; + Blinkers blinkers; + bool blink_state; + bool blinker (); + void start_blinking (ControllerID); + void stop_blinking (ControllerID); + + void set_current_stripable (std::shared_ptr); + void drop_current_stripable (); + /*void use_master (); + void use_monitor ();*/ + void stripable_selection_changed (); + /*PBD::ScopedConnection selection_connection;*/ + PBD::ScopedConnectionList stripable_connections; + PBD::ScopedConnectionList console1_connections; + + void map_stripable_state (); + + void notify_parameter_changed (std::string); + + /* operations (defined in c1_operations.cc) */ + + void bank (bool up); + void drive (uint32_t value); + void gain (const uint32_t value); + void mute (const uint32_t); + void pan (const uint32_t value); + void phase (const uint32_t); + void rude_solo (const uint32_t); + void select (const uint32_t i); + void shift (const uint32_t); + void solo (const uint32_t); + void trim (const uint32_t value); + void window (const uint32_t value); + void zoom (const uint32_t value); + + // Filter Section + void filter (const uint32_t value); + void low_cut (const uint32_t value); + void high_cut (const uint32_t value); + + // Gate Section + void gate (const uint32_t value); + void gate_scf (const uint32_t value); + void gate_listen (const uint32_t value); + void gate_thresh (const uint32_t value); + void gate_depth (const uint32_t value); + void gate_release (const uint32_t value); + void gate_attack (const uint32_t value); + void gate_hyst (const uint32_t value); + void gate_hold (const uint32_t value); + void gate_filter_freq (const uint32_t value); + + // EQ section + void eq (const uint32_t); + void eq_freq (const uint32_t band, uint32_t value); + void eq_gain (const uint32_t band, uint32_t value); + void eq_high_shape (const uint32_t value); + void eq_low_shape (const uint32_t value); + + ControllerID eq_freq_controller_for_band (const uint32_t band) + { + ControllerID eq_freq_id; + switch (band) { + case 0: + eq_freq_id = ControllerID::LOW_FREQ; + break; + case 1: + eq_freq_id = ControllerID::LOW_MID_FREQ; + break; + case 2: + eq_freq_id = ControllerID::HIGH_MID_FREQ; + break; + case 3: + eq_freq_id = ControllerID::HIGH_FREQ; + break; + } + return eq_freq_id; + } + + ControllerID eq_gain_controller_for_band (const uint32_t band) + { + ControllerID eq_gain_id; + switch (band) { + case 0: + eq_gain_id = ControllerID::LOW_GAIN; + break; + case 1: + eq_gain_id = ControllerID::LOW_MID_GAIN; + break; + case 2: + eq_gain_id = ControllerID::HIGH_MID_GAIN; + break; + case 3: + eq_gain_id = ControllerID::HIGH_GAIN; + break; + } + return eq_gain_id; + } + + // Mixbus sends + void mb_send_level (const uint32_t n, const uint32_t value); + + // Comp Section + void comp (const uint32_t value); + void comp_mode (const uint32_t value); + void comp_thresh (const uint32_t value); + void comp_attack (const uint32_t value); + void comp_release (const uint32_t value); + void comp_ratio (const uint32_t value); + void comp_makeup (const uint32_t value); + void comp_emph (const uint32_t value); + + bool map_encoder (ControllerID controllerID); + void map_encoder (ControllerID controllerID, std::shared_ptr control); + + void map_bank (); + void map_drive (); + void map_gain (); + void map_monitoring (); + void map_mute (); + void map_pan (); + void map_phase (); + void map_recenable (); + void map_select (); + void map_shift (bool shift); + void map_solo (); + void map_trim (); + + // Filter Section + void map_filter (); + void map_low_cut (); + void map_high_cut (); + + // Gate Section + void map_gate (); + void map_gate_scf (); + void map_gate_listen (); + void map_gate_thresh (); + void map_gate_depth (); + void map_gate_release (); + void map_gate_attack (); + void map_gate_hyst (); + void map_gate_hold (); + void map_gate_filter_freq (); + + // EQ section + void map_eq (); + void map_eq_freq (const uint32_t band); + void map_eq_gain (const uint32_t band); + void map_eq_low_shape (); + void map_eq_high_shape (); + + // MB Sends + void map_mb_send_level (const uint32_t n); + + // Comp Section + void map_comp (); + void map_comp_mode (); + void map_comp_thresh (); + void map_comp_attack (); + void map_comp_release (); + void map_comp_ratio (); + void map_comp_makeup (); + void map_comp_emph (); + + bool rec_enable_state; + + 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); +}; +} +#endif /* ardour_surface_console1_h */ \ No newline at end of file diff --git a/libs/surfaces/console1/console1_interface.cc b/libs/surfaces/console1/console1_interface.cc new file mode 100644 index 0000000000..0b04edc0bd --- /dev/null +++ b/libs/surfaces/console1/console1_interface.cc @@ -0,0 +1,61 @@ +/* + * 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 "control_protocol/control_protocol.h" +#include "console1.h" + +using namespace ARDOUR; +using namespace ArdourSurface; + +static ControlProtocol* +new_console1 (Session* s) +{ + Console1* console1 = 0; + + try { + console1 = new Console1 (*s); + } catch (failed_constructor& err) { + delete console1; + console1 = 0; + return 0; + } + + return console1; +} + +static void +delete_console1 (ControlProtocol* cp) +{ + delete cp; +} + +static ControlProtocolDescriptor console1_descriptor = { + /* name : */ "Softube Console1", + /* id : */ "uri://ardour.org/surfaces/console1:0", + /* module : */ 0, + /* available */ 0, + /* probe port : */ 0, + /* match usb */ 0, + /* initialize : */ new_console1, + /* destroy : */ delete_console1, +}; + +extern "C" ARDOURSURFACE_API ControlProtocolDescriptor* protocol_descriptor () { return &console1_descriptor; } + diff --git a/libs/surfaces/console1/wscript b/libs/surfaces/console1/wscript new file mode 100644 index 0000000000..36a18f6024 --- /dev/null +++ b/libs/surfaces/console1/wscript @@ -0,0 +1,33 @@ +#!/usr/bin/env python +from waflib.extras import autowaf as autowaf +import os + +# Mandatory variables +top = '.' +out = 'build' + +def options(opt): + autowaf.set_options(opt) + +def configure(conf): + pass + +def build(bld): + obj = bld(features = 'cxx cxxshlib') + obj.source = ''' + console1_interface.cc + console1.cc + c1_operations.cc + c1_gui.cc + ''' + obj.defines = [ 'PACKAGE="ardour_console1"' ] + obj.defines += [ 'ARDOURSURFACE_DLL_EXPORTS' ] + obj.includes = [ '.', './console1', './midi_surface/midi_surface'] + obj.name = 'libardour_console1' + obj.target = 'ardour_console1' + obj.uselib = 'GTKMM GTK GDK XML OSX' + obj.use = 'libardour libardour_cp libardour_midisurface libcanvas libgtkmm2ext libpbd' + obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces') + +def shutdown(): + autowaf.shutdown() diff --git a/libs/surfaces/wscript b/libs/surfaces/wscript index 47f05ee7de..2ca75b131f 100644 --- a/libs/surfaces/wscript +++ b/libs/surfaces/wscript @@ -28,6 +28,7 @@ children = [ 'us2400', 'launch_control_xl', 'osc', + 'console1' ] def options(opt): @@ -87,6 +88,7 @@ def build(bld): bld.recurse('us2400') bld.recurse('launch_control_xl') bld.recurse('osc') + bld.recurse('console1') if bld.is_defined('BUILD_WIIMOTE'): bld.recurse('wiimote')