1235 lines
41 KiB
C++
1235 lines
41 KiB
C++
/*
|
|
* Copyright (C) 2023 Holger Dehnhardt <holger@dehnhardt.org>
|
|
*
|
|
* 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 <chrono>
|
|
#include <thread>
|
|
|
|
#include <boost/optional.hpp>
|
|
#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/filesystem_paths.h"
|
|
#include "ardour/meter.h"
|
|
#include "ardour/monitor_control.h"
|
|
#include "ardour/phase_control.h"
|
|
#include "ardour/readonly_control.h"
|
|
#include "ardour/route.h"
|
|
#include "ardour/selection.h"
|
|
#include "ardour/session.h"
|
|
#include "ardour/stripable.h"
|
|
#include "ardour/track.h"
|
|
#include "ardour/vca_manager.h"
|
|
#include "c1_control.h"
|
|
#include "c1_gui.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 ();
|
|
|
|
for (const auto& b : buttons) {
|
|
delete b.second;
|
|
}
|
|
for (const auto& e : encoders) {
|
|
delete e.second;
|
|
}
|
|
for (const auto& m : meters) {
|
|
delete m.second;
|
|
}
|
|
for (const auto& mb : multi_buttons) {
|
|
delete mb.second;
|
|
}
|
|
|
|
/* 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 ();
|
|
|
|
} else {
|
|
/* Control Protocol Manager never calls us with false, but
|
|
* insteads destroys us.
|
|
*/
|
|
}
|
|
|
|
ControlProtocol::set_active (yn);
|
|
/* this needs to be done that early, otherwise we'll miss the call of the signal */
|
|
session->SessionLoaded.connect (
|
|
session_connections, MISSING_INVALIDATOR, boost::bind (&Console1::notify_session_loaded, this), this);
|
|
|
|
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
|
|
}
|
|
|
|
XMLNode&
|
|
Console1::get_state () const
|
|
{
|
|
XMLNode& node = MIDISurface::get_state ();
|
|
node.set_property ("swap-solo-mute", swap_solo_mute);
|
|
node.set_property ("create-mapping-stubs", create_mapping_stubs);
|
|
return node;
|
|
}
|
|
|
|
int
|
|
Console1::set_state (const XMLNode& node, int version)
|
|
{
|
|
MIDISurface::set_state (node, version);
|
|
std::string tmp;
|
|
node.get_property ("swap-solo-mute", tmp);
|
|
swap_solo_mute = (tmp == "1");
|
|
node.get_property ("create-mapping-stubs", tmp);
|
|
create_mapping_stubs = (tmp == "1");
|
|
return 0;
|
|
}
|
|
|
|
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");
|
|
|
|
/*
|
|
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
|
|
*/
|
|
|
|
if (_in_use)
|
|
return 0;
|
|
|
|
load_mappings ();
|
|
setup_controls ();
|
|
|
|
/*
|
|
Connection to the blink-timer
|
|
*/
|
|
Glib::RefPtr<Glib::TimeoutSource> 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<Glib::TimeoutSource> periodic_timer = Glib::TimeoutSource::create (100);
|
|
periodic_connection = periodic_timer->connect (sigc::mem_fun (*this, &Console1::periodic));
|
|
periodic_timer->attach (main_loop ()->get_context ());
|
|
connect_session_signals ();
|
|
connect_internal_signals ();
|
|
create_strip_inventory ();
|
|
_in_use = true;
|
|
|
|
DEBUG_TRACE (DEBUG::Console1, "************** begin_using_device() ********************\n");
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Console1::stop_using_device ()
|
|
{
|
|
DEBUG_TRACE (DEBUG::Console1, "stop_using_device()\n");
|
|
if (!_in_use)
|
|
return 0;
|
|
blink_connection.disconnect ();
|
|
periodic_connection.disconnect ();
|
|
stripable_connections.drop_connections ();
|
|
session_connections.drop_connections ();
|
|
console1_connections.drop_connections ();
|
|
_in_use = false;
|
|
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_inventory, this), this);
|
|
// receive VCAs added
|
|
session->vca_manager ().VCAAdded.connect (
|
|
session_connections, MISSING_INVALIDATOR, boost::bind (&Console1::create_strip_inventory, 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);
|
|
session->MonitorBusAddedOrRemoved.connect (
|
|
session_connections, MISSING_INVALIDATOR, boost::bind (&Console1::master_monitor_has_changed, this), this);
|
|
session->MonitorChanged.connect (
|
|
session_connections, MISSING_INVALIDATOR, boost::bind (&Console1::master_monitor_has_changed, this), this);
|
|
session->RouteAdded.connect (
|
|
session_connections, MISSING_INVALIDATOR, boost::bind (&Console1::strip_inventory_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 ()
|
|
{
|
|
DEBUG_TRACE (DEBUG::Console1, "connect_internal_signals\n");
|
|
BankChange.connect (console1_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_bank, this), this);
|
|
ShiftChange.connect (console1_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_shift, this, _1), this);
|
|
PluginStateChange.connect (
|
|
console1_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_plugin_state, this, _1), this);
|
|
GotoView.connect (
|
|
console1_connections,
|
|
MISSING_INVALIDATOR,
|
|
[] (uint32_t 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::notify_session_loaded ()
|
|
{
|
|
DEBUG_TRACE (DEBUG::Console1, "************** Session Loaded() ********************\n");
|
|
stripable_selection_changed ();
|
|
}
|
|
|
|
void
|
|
Console1::setup_controls ()
|
|
{
|
|
|
|
for (uint32_t i = 0; i < 20; ++i) {
|
|
new ControllerButton (this,
|
|
ControllerID (FOCUS1 + i),
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::select, this, i)),
|
|
0,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::select_plugin, this, i)));
|
|
}
|
|
|
|
new ControllerButton (
|
|
this, ControllerID::PRESET, boost::function<void (uint32_t)> (boost::bind (&Console1::shift, this, _1)));
|
|
|
|
new ControllerButton (this,
|
|
ControllerID::TRACK_GROUP,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::plugin_state, this, _1)));
|
|
|
|
new ControllerButton (
|
|
this, ControllerID::DISPLAY_ON, boost::function<void (uint32_t)> (boost::bind (&Console1::rude_solo, this, _1)));
|
|
new ControllerButton (
|
|
this, ControllerID::MODE, boost::function<void (uint32_t)> (boost::bind (&Console1::zoom, this, _1)));
|
|
new MultiStateButton (this,
|
|
ControllerID::EXTERNAL_SIDECHAIN,
|
|
std::vector<uint32_t>{ 0, 63, 127 },
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::window, this, _1)));
|
|
|
|
new ControllerButton (
|
|
this, ControllerID::PAGE_UP, boost::function<void (uint32_t)> (boost::bind (&Console1::bank, this, true)));
|
|
new ControllerButton (
|
|
this, ControllerID::PAGE_DOWN, boost::function<void (uint32_t)> (boost::bind (&Console1::bank, this, false)));
|
|
|
|
new ControllerButton (this,
|
|
swap_solo_mute ? ControllerID::SOLO : ControllerID::MUTE,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::mute, this, _1)));
|
|
new ControllerButton (this,
|
|
swap_solo_mute ? ControllerID::MUTE : ControllerID::SOLO,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::solo, this, _1)));
|
|
|
|
new ControllerButton (
|
|
this, ControllerID::PHASE_INV, boost::function<void (uint32_t)> (boost::bind (&Console1::phase, this, _1)));
|
|
|
|
/*
|
|
Console 1: Input Gain - Ardour / Mixbus: Trim
|
|
*/
|
|
new Encoder (this, ControllerID::GAIN, boost::function<void (uint32_t)> (boost::bind (&Console1::trim, this, _1)));
|
|
|
|
/*
|
|
Console 1: Volume - Ardour / Mixbus: Gain
|
|
*/
|
|
new Encoder (
|
|
this, ControllerID::VOLUME, boost::function<void (uint32_t)> (boost::bind (&Console1::gain, this, _1)));
|
|
|
|
new Encoder (this, ControllerID::PAN, boost::function<void (uint32_t)> (boost::bind (&Console1::pan, this, _1)));
|
|
|
|
/* Filter Section*/
|
|
new ControllerButton (this,
|
|
ControllerID::FILTER_TO_COMPRESSORS,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::filter, this, _1)));
|
|
new Encoder (
|
|
this, ControllerID::LOW_CUT, boost::function<void (uint32_t)> (boost::bind (&Console1::low_cut, this, _1)));
|
|
new Encoder (
|
|
this, ControllerID::HIGH_CUT, boost::function<void (uint32_t)> (boost::bind (&Console1::high_cut, this, _1)));
|
|
|
|
/* Gate Section */
|
|
new ControllerButton (
|
|
this, ControllerID::SHAPE, boost::function<void (uint32_t)> (boost::bind (&Console1::gate, this, _1)));
|
|
new ControllerButton (this,
|
|
ControllerID::HARD_GATE,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::gate_scf, this, _1)),
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::gate_listen, this, _1)));
|
|
new Encoder (this,
|
|
ControllerID::SHAPE_GATE,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::gate_thresh, this, _1)));
|
|
new Encoder (this,
|
|
ControllerID::SHAPE_RELEASE,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::gate_release, this, _1)),
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::gate_hyst, this, _1)));
|
|
new Encoder (this,
|
|
ControllerID::SHAPE_SUSTAIN,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::gate_attack, this, _1)),
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::gate_hold, this, _1)));
|
|
new Encoder (this,
|
|
ControllerID::SHAPE_PUNCH,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::gate_depth, this, _1)),
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::gate_filter_freq, this, _1)));
|
|
|
|
new Meter (this, ControllerID::SHAPE_METER, boost::function<void ()> ([] () {}));
|
|
|
|
/* EQ Section */
|
|
new ControllerButton (
|
|
this, ControllerID::EQ, boost::function<void (uint32_t)> (boost::bind (&Console1::eq, this, _1)));
|
|
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
new Encoder (this,
|
|
eq_freq_controller_for_band (i),
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::eq_freq, this, i, _1)),
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::mb_send_level, this, i, _1)));
|
|
new Encoder (this,
|
|
eq_gain_controller_for_band (i),
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::eq_gain, this, i, _1)),
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::mb_send_level, this, i + 4, _1)));
|
|
}
|
|
new Encoder (this,
|
|
ControllerID::LOW_MID_SHAPE,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::mb_send_level, this, 10, _1)),
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::mb_send_level, this, 8, _1)));
|
|
new Encoder (this,
|
|
ControllerID::HIGH_MID_SHAPE,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::mb_send_level, this, 11, _1)),
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::mb_send_level, this, 9, _1)));
|
|
|
|
new ControllerButton (this,
|
|
ControllerID::LOW_SHAPE,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::eq_low_shape, this, _1)));
|
|
new ControllerButton (this,
|
|
ControllerID::HIGH_SHAPE,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::eq_high_shape, this, _1)));
|
|
|
|
new Encoder (
|
|
this, ControllerID::CHARACTER, boost::function<void (uint32_t)> (boost::bind (&Console1::drive, this, _1)));
|
|
|
|
/* Compressor Section */
|
|
new ControllerButton (
|
|
this, ControllerID::COMP, boost::function<void (uint32_t)> (boost::bind (&Console1::comp, this, _1)));
|
|
new MultiStateButton (this,
|
|
ControllerID::ORDER,
|
|
std::vector<uint32_t>{ 0, 63, 127 },
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::comp_mode, this, _1)));
|
|
|
|
new Encoder (this,
|
|
ControllerID::COMP_THRESH,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::comp_thresh, this, _1)));
|
|
new Encoder (this,
|
|
ControllerID::COMP_ATTACK,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::comp_attack, this, _1)));
|
|
new Encoder (this,
|
|
ControllerID::COMP_RELEASE,
|
|
boost::function<void (uint32_t)> (boost::bind (&Console1::comp_release, this, _1)));
|
|
new Encoder (
|
|
this, ControllerID::COMP_RATIO, boost::function<void (uint32_t)> (boost::bind (&Console1::comp_ratio, this, _1)));
|
|
new Encoder (
|
|
this, ControllerID::COMP_PAR, boost::function<void (uint32_t)> (boost::bind (&Console1::comp_makeup, this, _1)));
|
|
new Encoder (
|
|
this, ControllerID::DRIVE, boost::function<void (uint32_t)> (boost::bind (&Console1::comp_emph, this, _1)));
|
|
|
|
new Meter (this, ControllerID::COMP_METER, boost::function<void ()> ([] () {}));
|
|
|
|
/* Output Section */
|
|
new Meter (this, ControllerID::OUTPUT_METER_L, boost::function<void ()> ([] () {}));
|
|
new Meter (this, ControllerID::OUTPUT_METER_R, boost::function<void ()> ([] () {}));
|
|
}
|
|
|
|
void
|
|
Console1::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* tb)
|
|
{
|
|
uint32_t controller_number = static_cast<uint32_t> (tb->controller_number);
|
|
uint32_t value = static_cast<uint32_t> (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 (in_plugin_state && e->plugin_action) {
|
|
e->plugin_action (value);
|
|
} else if (shift_state && e->shift_action) {
|
|
e->shift_action (value);
|
|
} else {
|
|
e->action (value);
|
|
}
|
|
return;
|
|
} catch (ControlNotFoundException const&) {
|
|
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 (in_plugin_state && b->plugin_action) {
|
|
DEBUG_TRACE (DEBUG::Console1, "Executing plugin_action\n");
|
|
b->plugin_action (value);
|
|
} else if (shift_state && b->shift_action) {
|
|
DEBUG_TRACE (DEBUG::Console1, "Executing shift_action\n");
|
|
b->shift_action (value);
|
|
} else {
|
|
DEBUG_TRACE (DEBUG::Console1, "Executing action\n");
|
|
b->action (value);
|
|
}
|
|
return;
|
|
} catch (ControlNotFoundException const&) {
|
|
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 const&) {
|
|
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 const&) {
|
|
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 ()
|
|
{
|
|
if (!_in_use)
|
|
return;
|
|
DEBUG_TRACE (DEBUG::Console1, "stripable_selection_changed \n");
|
|
std::shared_ptr<Stripable> r = ControlProtocol::first_selected_stripable ();
|
|
if (r)
|
|
set_current_stripable (r);
|
|
}
|
|
|
|
void
|
|
Console1::drop_current_stripable ()
|
|
{
|
|
DEBUG_TRACE (DEBUG::Console1, "drop_current_stripable \n");
|
|
if (_current_stripable) {
|
|
if (_current_stripable == session->monitor_out ()) {
|
|
set_current_stripable (session->master_out ());
|
|
} else {
|
|
set_current_stripable (_current_stripable);
|
|
}
|
|
} else {
|
|
set_current_stripable (std::shared_ptr<Stripable> ());
|
|
}
|
|
}
|
|
|
|
void
|
|
Console1::set_current_stripable (std::shared_ptr<Stripable> 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");
|
|
|
|
current_plugin_index = -1;
|
|
|
|
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<AutomationControl> 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);
|
|
|
|
if (_current_stripable->mute_control ()) {
|
|
_current_stripable->mute_control ()->Changed.connect (
|
|
stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_mute, this), this);
|
|
}
|
|
|
|
if (_current_stripable->solo_control ()) {
|
|
_current_stripable->solo_control ()->Changed.connect (
|
|
stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_solo, this), this);
|
|
}
|
|
|
|
if (_current_stripable->phase_control ()) {
|
|
_current_stripable->phase_control ()->Changed.connect (
|
|
stripable_connections, MISSING_INVALIDATOR, boost::bind (&Console1::map_phase, this), this);
|
|
}
|
|
|
|
// Rec Enabled
|
|
std::shared_ptr<Track> t = std::dynamic_pointer_cast<Track> (_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<AutomationControl> 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<AutomationControl> 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_shift (shift_state);
|
|
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 const&) {
|
|
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<ControllerButton*> (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<Meter*> (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<Encoder*> (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<MultiStateButton*> (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 const&) {
|
|
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 const&) {
|
|
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 const&) {
|
|
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<AutomationControl> actl = std::dynamic_pointer_cast<AutomationControl> (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<AutomationControl> actl = std::dynamic_pointer_cast<AutomationControl> (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_inventory ()
|
|
{
|
|
DEBUG_TRACE (DEBUG::Console1, "create_strip_inventory()\n");
|
|
boost::optional<order_t> master_order;
|
|
strip_inventory.clear ();
|
|
StripableList sl = session->get_stripables ();
|
|
uint32_t index = 0;
|
|
for (const auto& s : sl) {
|
|
PresentationInfo pi = s->presentation_info ();
|
|
DEBUG_TRACE (DEBUG::Console1, string_compose ("%1: ", s->name ()));
|
|
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 - ignoring\n", index, pi.order ()));
|
|
continue;
|
|
}
|
|
if (pi.flags () & ARDOUR::PresentationInfo::FoldbackBus) {
|
|
DEBUG_TRACE (DEBUG::Console1,
|
|
string_compose ("foldback bus 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) {
|
|
master_index = index;
|
|
strip_inventory.insert (std::make_pair (index, master_order.value ()));
|
|
}
|
|
max_strip_index = index;
|
|
DEBUG_TRACE (DEBUG::Console1,
|
|
string_compose ("create_strip_inventory - 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<uint32_t, order_t> i : strip_inventory) {
|
|
if (i.second == order) {
|
|
return i.first;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
Console1::select_rid_by_index (uint32_t index)
|
|
{
|
|
bool success = true;
|
|
DEBUG_TRACE (DEBUG::Console1, "select_rid_by_index()\n");
|
|
int offset = session->monitor_out () ? 1 : 0;
|
|
DEBUG_TRACE (DEBUG::Console1, string_compose ("offset %1\n", offset));
|
|
uint_fast32_t rid = 0;
|
|
#ifdef MIXBUS
|
|
rid = index + offset;
|
|
#else
|
|
if (index == master_index)
|
|
rid = 1;
|
|
else
|
|
rid = index + 1 + offset;
|
|
#endif
|
|
DEBUG_TRACE (DEBUG::Console1, string_compose ("rid %1\n", rid));
|
|
if (rid > (max_strip_index + 1 + offset))
|
|
success = false;
|
|
std::shared_ptr<Stripable> s = session->get_remote_nth_stripable (rid, PresentationInfo::MixerStripables);
|
|
if (s) {
|
|
session->selection ().select_stripable_and_maybe_group (s, true, false, 0);
|
|
} else {
|
|
success = false;
|
|
}
|
|
if (!success) {
|
|
map_select ();
|
|
}
|
|
}
|
|
|
|
void
|
|
Console1::master_monitor_has_changed ()
|
|
{
|
|
DEBUG_TRACE (DEBUG::Console1, "master_monitor_has_changed()\n");
|
|
bool monitor_active = session->monitor_active ();
|
|
DEBUG_TRACE (DEBUG::Console1, string_compose ("master_monitor_has_changed - monitor active %1\n", monitor_active));
|
|
create_strip_inventory ();
|
|
}
|