2020-02-20 07:12:36 -05:00
|
|
|
/*
|
2021-06-14 08:45:51 -04:00
|
|
|
* Copyright (C) 2020-2021 Luciano Iam <oss@lucianoiam.com>
|
2020-02-20 07:12:36 -05:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2020-02-23 10:03:59 -05:00
|
|
|
#include "ardour/plugin_insert.h"
|
2020-02-20 07:12:36 -05:00
|
|
|
#include "ardour/session.h"
|
|
|
|
#include "ardour/tempo.h"
|
|
|
|
|
2021-06-13 14:54:43 -04:00
|
|
|
#include "pbd/abstract_ui.cc" // instantiate template
|
|
|
|
|
2020-02-20 07:12:36 -05:00
|
|
|
#include "feedback.h"
|
2020-05-30 13:08:27 -04:00
|
|
|
#include "transport.h"
|
2020-02-20 07:12:36 -05:00
|
|
|
#include "server.h"
|
2020-02-23 10:03:59 -05:00
|
|
|
#include "state.h"
|
2020-02-20 07:12:36 -05:00
|
|
|
|
2020-04-22 05:26:51 -04:00
|
|
|
// TO DO: make this configurable
|
|
|
|
#define POLL_INTERVAL_MS 100
|
|
|
|
|
2020-02-20 07:12:36 -05:00
|
|
|
using namespace ARDOUR;
|
2020-08-30 15:15:07 -04:00
|
|
|
using namespace ArdourSurface;
|
2020-02-20 07:12:36 -05:00
|
|
|
|
2020-04-18 06:39:18 -04:00
|
|
|
struct TransportObserver {
|
|
|
|
void operator() (ArdourFeedback* p)
|
|
|
|
{
|
2020-05-30 13:08:27 -04:00
|
|
|
p->update_all (Node::transport_roll, p->transport ().roll ());
|
2020-04-18 06:39:18 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct RecordStateObserver {
|
|
|
|
void operator() (ArdourFeedback* p)
|
|
|
|
{
|
2020-05-30 13:08:27 -04:00
|
|
|
p->update_all (Node::transport_record, p->transport ().record ());
|
2020-04-18 06:39:18 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-02-22 06:49:52 -05:00
|
|
|
struct TempoObserver {
|
2020-02-23 10:03:59 -05:00
|
|
|
void operator() (ArdourFeedback* p)
|
|
|
|
{
|
2020-05-30 13:08:27 -04:00
|
|
|
p->update_all (Node::transport_tempo, p->transport ().tempo ());
|
2020-02-23 10:03:59 -05:00
|
|
|
}
|
2020-02-22 06:49:52 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
struct StripGainObserver {
|
2020-08-29 18:42:07 -04:00
|
|
|
void operator() (ArdourFeedback* p, uint32_t strip_id)
|
2020-02-23 10:03:59 -05:00
|
|
|
{
|
|
|
|
// fires multiple times (4x as of ardour 6.0)
|
2020-08-29 18:42:07 -04:00
|
|
|
p->update_all (Node::strip_gain, strip_id, p->mixer ().strip (strip_id).gain ());
|
2020-02-23 10:03:59 -05:00
|
|
|
}
|
2020-02-22 06:49:52 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
struct StripPanObserver {
|
2020-08-29 18:42:07 -04:00
|
|
|
void operator() (ArdourFeedback* p, uint32_t strip_id)
|
2020-02-23 10:03:59 -05:00
|
|
|
{
|
2020-08-29 18:42:07 -04:00
|
|
|
p->update_all (Node::strip_pan, strip_id, p->mixer ().strip (strip_id).pan ());
|
2020-02-23 10:03:59 -05:00
|
|
|
}
|
2020-02-22 06:49:52 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
struct StripMuteObserver {
|
2020-08-29 18:42:07 -04:00
|
|
|
void operator() (ArdourFeedback* p, uint32_t strip_id)
|
2020-02-23 10:03:59 -05:00
|
|
|
{
|
2020-08-29 18:42:07 -04:00
|
|
|
p->update_all (Node::strip_mute, strip_id, p->mixer ().strip (strip_id).mute ());
|
2020-02-23 10:03:59 -05:00
|
|
|
}
|
2020-02-22 06:49:52 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
struct PluginBypassObserver {
|
2020-08-29 18:42:07 -04:00
|
|
|
void operator() (ArdourFeedback* p, uint32_t strip_id, uint32_t plugin_id)
|
2020-02-23 10:03:59 -05:00
|
|
|
{
|
2020-08-29 18:42:07 -04:00
|
|
|
p->update_all (Node::strip_plugin_enable, strip_id, plugin_id,
|
|
|
|
p->mixer ().strip (strip_id).plugin (plugin_id).enabled ());
|
2020-02-23 10:03:59 -05:00
|
|
|
}
|
2020-02-22 06:49:52 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
struct PluginParamValueObserver {
|
2020-08-29 18:42:07 -04:00
|
|
|
void operator() (ArdourFeedback* p, uint32_t strip_id, uint32_t plugin_id,
|
|
|
|
uint32_t param_id, boost::weak_ptr<AutomationControl> ctrl)
|
2020-02-23 10:03:59 -05:00
|
|
|
{
|
2020-02-25 16:55:59 -05:00
|
|
|
boost::shared_ptr<AutomationControl> control = ctrl.lock ();
|
2020-08-29 14:06:00 -04:00
|
|
|
|
2020-02-25 16:55:59 -05:00
|
|
|
if (!control) {
|
|
|
|
return;
|
|
|
|
}
|
2022-01-09 16:19:20 -05:00
|
|
|
|
2020-08-29 18:42:07 -04:00
|
|
|
p->update_all (Node::strip_plugin_param_value, strip_id, plugin_id, param_id,
|
2020-08-29 11:01:35 -04:00
|
|
|
ArdourMixerPlugin::param_value (control));
|
2020-02-23 10:03:59 -05:00
|
|
|
}
|
2020-02-22 06:49:52 -05:00
|
|
|
};
|
2020-02-20 07:12:36 -05:00
|
|
|
|
2021-06-13 14:54:43 -04:00
|
|
|
FeedbackHelperUI::FeedbackHelperUI()
|
2021-06-13 19:07:40 -04:00
|
|
|
: AbstractUI<BaseUI::BaseRequestObject> ("WS_FeedbackHelperUI")
|
2021-06-13 14:54:43 -04:00
|
|
|
{
|
2022-12-29 22:50:03 -05:00
|
|
|
// This renames and changes the event loop for the main thread, presumably
|
|
|
|
// this is not the actually desired behavior. If this is intentional, at
|
|
|
|
// least the event loop should be set back when this FeedbackHelperUI is
|
|
|
|
// destroyed, as otherwise future access to the event loop is likely going
|
|
|
|
// to crash.
|
2021-06-17 07:32:10 -04:00
|
|
|
char name[64];
|
|
|
|
snprintf (name, 64, "WS-%p", (void*)DEBUG_THREAD_SELF);
|
2021-06-13 19:07:40 -04:00
|
|
|
pthread_set_name (name);
|
2021-06-13 14:54:43 -04:00
|
|
|
set_event_loop_for_thread (this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
FeedbackHelperUI::do_request (BaseUI::BaseRequestObject* req) {
|
|
|
|
if (req->type == CallSlot) {
|
|
|
|
call_slot (MISSING_INVALIDATOR, req->the_slot);
|
|
|
|
} else if (req->type == Quit) {
|
|
|
|
quit ();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-02-20 07:12:36 -05:00
|
|
|
int
|
|
|
|
ArdourFeedback::start ()
|
|
|
|
{
|
2020-05-30 13:08:27 -04:00
|
|
|
observe_transport ();
|
|
|
|
observe_mixer ();
|
2020-02-20 07:12:36 -05:00
|
|
|
|
2020-08-29 14:06:00 -04:00
|
|
|
// some values need polling like the strip meters
|
2020-04-22 05:26:51 -04:00
|
|
|
Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (POLL_INTERVAL_MS);
|
2020-02-23 10:03:59 -05:00
|
|
|
_periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this,
|
|
|
|
&ArdourFeedback::poll));
|
2021-06-13 14:54:43 -04:00
|
|
|
|
|
|
|
// server must be started before feedback otherwise
|
2021-06-14 08:45:51 -04:00
|
|
|
// read_blocks_event_loop() will always return false
|
2021-06-13 17:49:13 -04:00
|
|
|
if (server ().read_blocks_event_loop ()) {
|
2021-06-13 14:54:43 -04:00
|
|
|
_helper.run();
|
|
|
|
periodic_timeout->attach (_helper.main_loop()->get_context ());
|
2021-06-13 17:49:13 -04:00
|
|
|
} else {
|
|
|
|
periodic_timeout->attach (main_loop ()->get_context ());
|
2021-06-13 14:54:43 -04:00
|
|
|
}
|
2020-02-20 07:12:36 -05:00
|
|
|
|
2020-02-23 10:03:59 -05:00
|
|
|
return 0;
|
2020-02-20 07:12:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ArdourFeedback::stop ()
|
|
|
|
{
|
2021-06-13 17:49:13 -04:00
|
|
|
if (server ().read_blocks_event_loop ()) {
|
2021-06-13 14:54:43 -04:00
|
|
|
_helper.quit();
|
|
|
|
}
|
|
|
|
|
2020-02-23 10:03:59 -05:00
|
|
|
_periodic_connection.disconnect ();
|
2020-08-02 09:41:36 -04:00
|
|
|
_transport_connections.drop_connections ();
|
2022-01-09 16:19:20 -05:00
|
|
|
|
2020-02-23 10:03:59 -05:00
|
|
|
return 0;
|
2020-02-20 07:12:36 -05:00
|
|
|
}
|
|
|
|
|
2020-02-22 06:49:52 -05:00
|
|
|
void
|
|
|
|
ArdourFeedback::update_all (std::string node, TypedValue value) const
|
|
|
|
{
|
2020-02-23 10:03:59 -05:00
|
|
|
update_all (node, ADDR_NONE, ADDR_NONE, ADDR_NONE, value);
|
2020-02-22 06:49:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-08-29 18:42:07 -04:00
|
|
|
ArdourFeedback::update_all (std::string node, uint32_t strip_id, TypedValue value) const
|
2020-02-22 06:49:52 -05:00
|
|
|
{
|
2020-08-29 18:42:07 -04:00
|
|
|
update_all (node, strip_id, ADDR_NONE, ADDR_NONE, value);
|
2020-02-22 06:49:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-08-29 18:42:07 -04:00
|
|
|
ArdourFeedback::update_all (std::string node, uint32_t strip_id, uint32_t plugin_id,
|
2020-02-23 10:03:59 -05:00
|
|
|
TypedValue value) const
|
2020-02-22 06:49:52 -05:00
|
|
|
{
|
2020-08-29 18:42:07 -04:00
|
|
|
update_all (node, strip_id, plugin_id, ADDR_NONE, value);
|
2020-02-22 06:49:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-08-29 18:42:07 -04:00
|
|
|
ArdourFeedback::update_all (std::string node, uint32_t strip_id, uint32_t plugin_id, uint32_t param_id,
|
2020-02-23 10:03:59 -05:00
|
|
|
TypedValue value) const
|
2020-02-22 06:49:52 -05:00
|
|
|
{
|
2020-02-23 10:03:59 -05:00
|
|
|
AddressVector addr = AddressVector ();
|
2020-02-22 06:49:52 -05:00
|
|
|
|
2020-08-29 18:42:07 -04:00
|
|
|
if (strip_id != ADDR_NONE) {
|
|
|
|
addr.push_back (strip_id);
|
2020-02-23 10:03:59 -05:00
|
|
|
}
|
2020-02-22 06:49:52 -05:00
|
|
|
|
2020-08-29 18:42:07 -04:00
|
|
|
if (plugin_id != ADDR_NONE) {
|
|
|
|
addr.push_back (plugin_id);
|
2020-02-23 10:03:59 -05:00
|
|
|
}
|
2020-02-22 06:49:52 -05:00
|
|
|
|
2020-08-29 18:42:07 -04:00
|
|
|
if (param_id != ADDR_NONE) {
|
|
|
|
addr.push_back (param_id);
|
2020-02-23 10:03:59 -05:00
|
|
|
}
|
2020-02-22 06:49:52 -05:00
|
|
|
|
2020-02-23 10:03:59 -05:00
|
|
|
ValueVector val = ValueVector ();
|
|
|
|
val.push_back (value);
|
2020-02-22 06:49:52 -05:00
|
|
|
|
2020-02-23 10:03:59 -05:00
|
|
|
server ().update_all_clients (NodeState (node, addr, val), false);
|
2020-02-22 06:49:52 -05:00
|
|
|
}
|
|
|
|
|
2021-06-13 17:49:13 -04:00
|
|
|
PBD::EventLoop*
|
|
|
|
ArdourFeedback::event_loop () const
|
|
|
|
{
|
|
|
|
if (server ().read_blocks_event_loop ()) {
|
|
|
|
return static_cast<PBD::EventLoop*> (&_helper);
|
|
|
|
} else {
|
|
|
|
return SurfaceComponent::event_loop ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-20 07:12:36 -05:00
|
|
|
bool
|
|
|
|
ArdourFeedback::poll () const
|
|
|
|
{
|
2020-05-30 13:08:27 -04:00
|
|
|
update_all (Node::transport_time, transport ().time ());
|
2022-01-09 16:19:20 -05:00
|
|
|
update_all (Node::transport_bbt, transport ().bbt ());
|
2020-04-18 08:56:46 -04:00
|
|
|
|
2020-08-29 16:41:47 -04:00
|
|
|
Glib::Threads::Mutex::Lock lock (mixer ().mutex ());
|
|
|
|
|
|
|
|
for (ArdourMixer::StripMap::iterator it = mixer ().strips ().begin (); it != mixer ().strips ().end (); ++it) {
|
2020-08-30 16:50:25 -04:00
|
|
|
double db = it->second->meter_level_db ();
|
|
|
|
update_all (Node::strip_meter, it->first, db);
|
2020-02-23 10:03:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2020-02-20 07:12:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-05-30 13:08:27 -04:00
|
|
|
ArdourFeedback::observe_transport ()
|
2020-02-20 07:12:36 -05:00
|
|
|
{
|
2020-04-18 06:39:18 -04:00
|
|
|
ARDOUR::Session& sess = session ();
|
2020-08-02 09:41:36 -04:00
|
|
|
sess.TransportStateChange.connect (_transport_connections, MISSING_INVALIDATOR,
|
2020-04-18 06:39:18 -04:00
|
|
|
boost::bind<void> (TransportObserver (), this), event_loop ());
|
2020-08-02 09:41:36 -04:00
|
|
|
sess.RecordStateChanged.connect (_transport_connections, MISSING_INVALIDATOR,
|
2020-04-18 06:39:18 -04:00
|
|
|
boost::bind<void> (RecordStateObserver (), this), event_loop ());
|
2020-11-27 14:40:58 -05:00
|
|
|
|
2020-12-31 00:27:37 -05:00
|
|
|
Temporal::TempoMap::MapChanged.connect (_transport_connections, MISSING_INVALIDATOR, boost::bind<void> (TempoObserver (), this), event_loop ());
|
2020-02-20 07:12:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-05-30 13:08:27 -04:00
|
|
|
ArdourFeedback::observe_mixer ()
|
2020-02-20 07:12:36 -05:00
|
|
|
{
|
2020-08-29 14:06:00 -04:00
|
|
|
for (ArdourMixer::StripMap::iterator it = mixer().strips().begin(); it != mixer().strips().end(); ++it) {
|
2020-08-30 16:50:25 -04:00
|
|
|
uint32_t strip_id = it->first;
|
|
|
|
boost::shared_ptr<ArdourMixerStrip> strip = it->second;
|
2020-08-29 14:06:00 -04:00
|
|
|
|
2020-08-30 16:50:25 -04:00
|
|
|
boost::shared_ptr<Stripable> stripable = strip->stripable ();
|
2020-08-02 09:41:36 -04:00
|
|
|
|
2020-08-31 01:41:11 -04:00
|
|
|
stripable->gain_control ()->Changed.connect (*it->second, MISSING_INVALIDATOR,
|
2020-08-29 18:42:07 -04:00
|
|
|
boost::bind<void> (StripGainObserver (), this, strip_id), event_loop ());
|
2020-02-20 07:12:36 -05:00
|
|
|
|
2020-08-29 11:01:35 -04:00
|
|
|
if (stripable->pan_azimuth_control ()) {
|
2020-08-31 01:41:11 -04:00
|
|
|
stripable->pan_azimuth_control ()->Changed.connect (*it->second, MISSING_INVALIDATOR,
|
2020-08-29 18:42:07 -04:00
|
|
|
boost::bind<void> (StripPanObserver (), this, strip_id), event_loop ());
|
2020-02-23 10:03:59 -05:00
|
|
|
}
|
2020-02-20 07:12:36 -05:00
|
|
|
|
2020-08-31 01:41:11 -04:00
|
|
|
stripable->mute_control ()->Changed.connect (*it->second, MISSING_INVALIDATOR,
|
2020-08-29 18:42:07 -04:00
|
|
|
boost::bind<void> (StripMuteObserver (), this, strip_id), event_loop ());
|
2020-11-27 14:40:58 -05:00
|
|
|
|
2020-08-30 16:50:25 -04:00
|
|
|
observe_strip_plugins (strip_id, strip->plugins ());
|
2020-02-23 10:03:59 -05:00
|
|
|
}
|
2020-02-20 07:12:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-08-29 18:42:07 -04:00
|
|
|
ArdourFeedback::observe_strip_plugins (uint32_t strip_id, ArdourMixerStrip::PluginMap& plugins)
|
2020-02-20 07:12:36 -05:00
|
|
|
{
|
2020-08-29 14:06:00 -04:00
|
|
|
for (ArdourMixerStrip::PluginMap::iterator it = plugins.begin(); it != plugins.end(); ++it) {
|
2020-09-05 07:12:04 -04:00
|
|
|
uint32_t plugin_id = it->first;
|
|
|
|
boost::shared_ptr<ArdourMixerPlugin> plugin = it->second;
|
|
|
|
boost::shared_ptr<PluginInsert> insert = plugin->insert ();
|
|
|
|
uint32_t bypass = insert->plugin ()->designated_bypass_port ();
|
|
|
|
Evoral::Parameter param = Evoral::Parameter (PluginAutomation, 0, bypass);
|
|
|
|
boost::shared_ptr<AutomationControl> control = insert->automation_control (param);
|
2020-02-23 10:03:59 -05:00
|
|
|
|
|
|
|
if (control) {
|
2020-08-31 01:41:11 -04:00
|
|
|
control->Changed.connect (*plugin, MISSING_INVALIDATOR,
|
2020-08-29 18:42:07 -04:00
|
|
|
boost::bind<void> (PluginBypassObserver (), this, strip_id, plugin_id), event_loop ());
|
2020-02-23 10:03:59 -05:00
|
|
|
}
|
|
|
|
|
2020-08-30 16:50:25 -04:00
|
|
|
for (uint32_t param_id = 0; param_id < plugin->param_count (); ++param_id) {
|
2020-08-29 18:42:07 -04:00
|
|
|
try {
|
2020-08-30 16:50:25 -04:00
|
|
|
boost::shared_ptr<AutomationControl> control = plugin->param_control (param_id);
|
2020-08-29 18:42:07 -04:00
|
|
|
|
2020-08-31 01:41:11 -04:00
|
|
|
control->Changed.connect (*plugin, MISSING_INVALIDATOR,
|
2020-08-29 18:42:07 -04:00
|
|
|
boost::bind<void> (PluginParamValueObserver (), this, strip_id, plugin_id, param_id,
|
|
|
|
boost::weak_ptr<AutomationControl>(control)),
|
|
|
|
event_loop ());
|
2020-08-30 15:15:33 -04:00
|
|
|
} catch (ArdourMixerNotFoundException& e) {
|
2020-08-29 18:42:07 -04:00
|
|
|
/* ignore */
|
|
|
|
}
|
2020-08-29 14:06:00 -04:00
|
|
|
}
|
2020-08-02 09:41:36 -04:00
|
|
|
}
|
|
|
|
}
|