ardour/libs/ardour/surround_pannable.cc

277 lines
9.0 KiB
C++

/*
* Copyright (C) 2023 Robin Gareus <robin@gareus.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 "pbd/error.h"
#include "ardour/automation_list.h"
#include "ardour/surround_pannable.h"
#include "ardour/session.h"
#include "ardour/value_as_string.h"
#include "pbd/i18n.h"
using namespace std;
using namespace PBD;
using namespace ARDOUR;
SurroundControllable::SurroundControllable (Session& s, Evoral::Parameter param, Temporal::TimeDomainProvider const& tdp)
: AutomationControl (s,
param,
ParameterDescriptor(param),
std::shared_ptr<AutomationList>(new AutomationList(param, tdp)))
{
}
std::string
SurroundControllable::get_user_string () const
{
float v = get_value ();
char buf[32];
switch (parameter ().type ()) {
case PanSurroundX:
if (v == 0.5) {
return _("Center");
}
snprintf(buf, sizeof(buf), "L%3d R%3d", (int)rint (100.0 * (1.0 - v)), (int)rint (100.0 * v));
break;
case PanSurroundY:
snprintf(buf, sizeof(buf), "F%3d B%3d", (int)rint (100.0 * (1.0 - v)), (int)rint (100.0 * v));
break;
case PanSurroundSize:
snprintf(buf, sizeof(buf), "%.0f%%", 100.f * v);
break;
default:
return value_as_string (desc(), v);
}
return buf;
}
SurroundPannable::SurroundPannable (Session& s, uint32_t chn, Temporal::TimeDomainProvider const & tdp)
: Automatable (s, tdp)
, SessionHandleRef (s)
, pan_pos_x (new SurroundControllable (s, Evoral::Parameter (PanSurroundX, 0, chn), tdp))
, pan_pos_y (new SurroundControllable (s, Evoral::Parameter (PanSurroundY, 0, chn), tdp))
, pan_pos_z (new SurroundControllable (s, Evoral::Parameter (PanSurroundZ, 0, chn), tdp))
, pan_size (new SurroundControllable (s, Evoral::Parameter (PanSurroundSize, 0, chn), tdp))
, pan_snap (new SurroundControllable (s, Evoral::Parameter (PanSurroundSnap, 0, chn), tdp))
, binaural_render_mode (new SurroundControllable (s, Evoral::Parameter (BinauralRenderMode, 0, chn), tdp))
, sur_elevation_enable (new SurroundControllable (s, Evoral::Parameter (PanSurroundElevationEnable, 0, chn), tdp))
, sur_zones (new SurroundControllable (s, Evoral::Parameter (PanSurroundZones, 0, chn), tdp))
, sur_ramp (new SurroundControllable (s, Evoral::Parameter (PanSurroundRamp, 0, chn), tdp))
, _auto_state (Off)
, _responding_to_control_auto_state_change (0)
{
binaural_render_mode->set_flag (Controllable::NotAutomatable);
add_control (pan_pos_x);
add_control (pan_pos_y);
add_control (pan_pos_z);
add_control (pan_size);
add_control (pan_snap);
add_control (binaural_render_mode); // not automatable
add_control (sur_elevation_enable); // hidden, volatile
add_control (sur_zones); // hidden, volatile
add_control (sur_ramp); // hidden, volatile
/* all controls change state together */
pan_pos_x->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&SurroundPannable::control_auto_state_changed, this, _1));
pan_pos_y->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&SurroundPannable::control_auto_state_changed, this, _1));
pan_pos_z->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&SurroundPannable::control_auto_state_changed, this, _1));
pan_size->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&SurroundPannable::control_auto_state_changed, this, _1));
pan_snap->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&SurroundPannable::control_auto_state_changed, this, _1));
pan_pos_x->Changed.connect_same_thread (*this, boost::bind (&SurroundPannable::value_changed, this));
pan_pos_y->Changed.connect_same_thread (*this, boost::bind (&SurroundPannable::value_changed, this));
pan_pos_z->Changed.connect_same_thread (*this, boost::bind (&SurroundPannable::value_changed, this));
pan_size->Changed.connect_same_thread (*this, boost::bind (&SurroundPannable::value_changed, this));
pan_snap->Changed.connect_same_thread (*this, boost::bind (&SurroundPannable::value_changed, this));
setup_visual_links ();
}
SurroundPannable::~SurroundPannable ()
{
}
void
SurroundPannable::setup_visual_links ()
{
/* all controls are visible together */
pan_pos_x->add_visually_linked_control (pan_pos_y);
pan_pos_x->add_visually_linked_control (pan_pos_z);
pan_pos_y->add_visually_linked_control (pan_pos_x);
pan_pos_y->add_visually_linked_control (pan_pos_z);
pan_pos_z->add_visually_linked_control (pan_pos_x);
pan_pos_z->add_visually_linked_control (pan_pos_y);
}
void
SurroundPannable::sync_visual_link_to (std::shared_ptr<SurroundPannable> other)
{
pan_pos_x->add_visually_linked_control (other->pan_pos_x);
pan_pos_x->add_visually_linked_control (other->pan_pos_y);
pan_pos_x->add_visually_linked_control (other->pan_pos_z);
pan_pos_y->add_visually_linked_control (other->pan_pos_x);
pan_pos_y->add_visually_linked_control (other->pan_pos_y);
pan_pos_y->add_visually_linked_control (other->pan_pos_z);
pan_pos_z->add_visually_linked_control (other->pan_pos_x);
pan_pos_z->add_visually_linked_control (other->pan_pos_y);
pan_pos_z->add_visually_linked_control (other->pan_pos_z);
}
void
SurroundPannable::sync_auto_state_with (std::shared_ptr<SurroundPannable> other)
{
other->pan_pos_x->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&SurroundPannable::control_auto_state_changed, this, _1));
}
void
SurroundPannable::foreach_pan_control (boost::function<void(std::shared_ptr<AutomationControl>)> f) const
{
f (pan_pos_x);
f (pan_pos_y);
f (pan_pos_z);
f (pan_size);
f (pan_snap);
f (sur_elevation_enable);
f (sur_zones);
f (sur_ramp);
}
void
SurroundPannable::control_auto_state_changed (AutoState new_state)
{
if (_responding_to_control_auto_state_change || _auto_state == new_state) {
return;
}
_responding_to_control_auto_state_change++;
foreach_pan_control ([new_state](std::shared_ptr<AutomationControl> ac) {
ac->set_automation_state (new_state);
});
_responding_to_control_auto_state_change--;
_auto_state = new_state;
automation_state_changed (new_state); /* EMIT SIGNAL */
}
void
SurroundPannable::value_changed ()
{
_session.set_dirty ();
}
void
SurroundPannable::set_automation_state (AutoState state)
{
if (state == _auto_state) {
return;
}
_auto_state = state;
const Controls& c (controls());
for (Controls::const_iterator ci = c.begin(); ci != c.end(); ++ci) {
std::shared_ptr<AutomationControl> ac = std::dynamic_pointer_cast<AutomationControl>(ci->second);
if (ac) {
ac->alist()->set_automation_state (state);
}
}
_session.set_dirty ();
automation_state_changed (_auto_state); /* EMIT SIGNAL */
}
bool
SurroundPannable::touching () const
{
const Controls& c (controls());
for (auto const& i : c) {
std::shared_ptr<AutomationControl> ac = std::dynamic_pointer_cast<AutomationControl>(i.second);
if (ac && ac->touching ()) {
return true;
}
}
return false;
}
XMLNode&
SurroundPannable::get_state () const
{
return state ();
}
XMLNode&
SurroundPannable::state () const
{
XMLNode* node = new XMLNode (X_("SurroundPannable"));
node->set_property ("channel", pan_pos_x->parameter ().id ());
node->add_child_nocopy (pan_pos_x->get_state());
node->add_child_nocopy (pan_pos_y->get_state());
node->add_child_nocopy (pan_pos_z->get_state());
node->add_child_nocopy (pan_size->get_state());
node->add_child_nocopy (pan_snap->get_state());
node->add_child_nocopy (binaural_render_mode->get_state());
return *node;
}
int
SurroundPannable::set_state (const XMLNode& root, int version)
{
if (root.name() != X_("SurroundPannable")) {
return -1;
}
const XMLNodeList& nlist (root.children());
XMLNodeConstIterator niter;
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
if ((*niter)->name() != Controllable::xml_node_name) {
continue;
}
std::string control_name;
if (!(*niter)->get_property (X_("name"), control_name)) {
continue;
}
if (control_name == pan_pos_x->name()) {
pan_pos_x->set_state (**niter, version);
} else if (control_name == pan_pos_y->name()) {
pan_pos_y->set_state (**niter, version);
} else if (control_name == pan_pos_z->name()) {
pan_pos_z->set_state (**niter, version);
} else if (control_name == pan_size->name()) {
pan_size->set_state (**niter, version);
} else if (control_name == pan_snap->name()) {
pan_snap->set_state (**niter, version);
} else if (control_name == binaural_render_mode->name()) {
binaural_render_mode->set_state (**niter, version);
}
}
return 0;
}