13
0
livetrax/libs/panners/1in2out/panner_1in2out.cc

375 lines
9.6 KiB
C++
Raw Normal View History

/*
* Copyright (C) 2011-2012 Carl Hetherington <carl@carlh.net>
* Copyright (C) 2011-2017 Paul Davis <paul@linuxaudiosystems.com>
* Copyright (C) 2013 John Emmas <john@creativepost.co.uk>
* Copyright (C) 2014-2015 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 <inttypes.h>
#include <cerrno>
2020-03-23 11:46:03 -04:00
#include <cmath>
#include <cstdio>
2020-03-23 11:46:03 -04:00
#include <cstdlib>
#include <float.h>
#include <locale.h>
2020-03-23 11:46:03 -04:00
#include <string>
#include <unistd.h>
#include <glibmm.h>
#include "pbd/cartesian.h"
#include "pbd/convert.h"
2020-03-23 11:46:03 -04:00
#include "pbd/enumwriter.h"
#include "pbd/error.h"
#include "pbd/failed_constructor.h"
#include "pbd/xml++.h"
#include "evoral/Curve.h"
#include "ardour/audio_buffer.h"
#include "ardour/buffer_set.h"
2020-03-23 11:46:03 -04:00
#include "ardour/debug.h"
#include "ardour/pannable.h"
2020-03-23 11:46:03 -04:00
#include "ardour/panner.h"
#include "ardour/profile.h"
2020-03-23 11:46:03 -04:00
#include "ardour/runtime_functions.h"
#include "ardour/session.h"
#include "ardour/utils.h"
#include "panner_1in2out.h"
2020-03-23 11:46:03 -04:00
#include "pbd/i18n.h"
#include "pbd/mathfix.h"
using namespace std;
using namespace ARDOUR;
using namespace PBD;
static PanPluginDescriptor _descriptor = {
2020-03-23 11:46:03 -04:00
"Mono to Stereo Panner",
"http://ardour.org/plugin/panner_1in2out",
"http://ardour.org/plugin/panner_1in2out#ui",
1, 2,
20,
Panner1in2out::factory
};
2020-03-23 11:46:03 -04:00
extern "C" ARDOURPANNER_API PanPluginDescriptor*
panner_descriptor ()
{
return &_descriptor;
}
Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p)
: Panner (p)
{
2019-09-25 15:02:31 -04:00
if (!_pannable->has_state ()) {
_pannable->pan_azimuth_control->set_value (0.5, Controllable::NoGroup);
}
2020-03-23 11:46:03 -04:00
_can_automate_list.insert (Evoral::Parameter (PanAzimuthAutomation));
2020-03-23 11:46:03 -04:00
update ();
2020-03-23 11:46:03 -04:00
left = desired_left;
right = desired_right;
left_interp = left;
right_interp = right;
2020-03-23 11:46:03 -04:00
_pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this));
}
Panner1in2out::~Panner1in2out ()
{
}
void
Panner1in2out::update ()
{
#if 0
2020-03-23 11:46:03 -04:00
float const pan_law_attenuation = -3.0f;
float const scale = 2.0f - 4.0f * powf (10.0f, pan_law_attenuation / 20.0f);
#else
float const scale = -0.831783138f;
#endif
float const panR = _pannable->pan_azimuth_control->get_value ();
float const panL = 1 - panR;
2020-03-23 11:46:03 -04:00
desired_left = panL * (scale * panL + 1.0f - scale);
desired_right = panR * (scale * panR + 1.0f - scale);
}
void
Panner1in2out::set_position (double p)
{
2020-03-23 11:46:03 -04:00
if (clamp_position (p)) {
_pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup);
}
}
bool
Panner1in2out::clamp_position (double& p)
{
2020-03-23 11:46:03 -04:00
/* any position between 0.0 and 1.0 is legal */
DEBUG_TRACE (DEBUG::Panning, string_compose ("want to move panner to %1 - always allowed in 0.0-1.0 range\n", p));
p = max (min (p, 1.0), 0.0);
return true;
}
pair<double, double>
Panner1in2out::position_range () const
{
return make_pair (0, 1);
}
double
Panner1in2out::position () const
{
2020-03-23 11:46:03 -04:00
return _pannable->pan_azimuth_control->get_value ();
}
void
Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */)
{
2020-03-23 11:46:03 -04:00
assert (obufs.count ().n_audio () == 2);
2020-03-23 11:46:03 -04:00
pan_t delta;
Sample* dst;
2020-03-23 11:46:03 -04:00
pan_t pan;
2020-03-23 11:46:03 -04:00
Sample* const src = srcbuf.data ();
/* LEFT OUTPUT */
2020-03-23 11:46:03 -04:00
dst = obufs.get_audio (0).data ();
if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
/* we've moving the pan by an appreciable amount, so we must
2020-03-23 11:46:03 -04:00
* interpolate over 64 samples or nframes, whichever is smaller */
2020-03-23 11:46:03 -04:00
pframes_t const limit = min ((pframes_t)64, nframes);
pframes_t n;
2020-03-23 11:46:03 -04:00
delta = -(delta / (float)(limit));
for (n = 0; n < limit; n++) {
left_interp = left_interp + delta;
2020-03-23 11:46:03 -04:00
left = left_interp + 0.9 * (left - left_interp);
dst[n] += src[n] * left * gain_coeff;
}
/* then pan the rest of the buffer; no need for interpolation for this bit */
pan = left * gain_coeff;
2020-03-23 11:46:03 -04:00
mix_buffers_with_gain (dst + n, src + n, nframes - n, pan);
} else {
2020-03-23 11:46:03 -04:00
left = desired_left;
left_interp = left;
if ((pan = (left * gain_coeff)) != 1.0f) {
if (pan != 0.0f) {
/* pan is 1 but also not 0, so we must do it "properly" */
2020-03-23 11:46:03 -04:00
mix_buffers_with_gain (dst, src, nframes, pan);
2020-03-23 11:46:03 -04:00
/* XXX it would be nice to mark that we wrote into the buffer */
}
} else {
/* pan is 1 so we can just copy the input samples straight in */
2020-03-23 11:46:03 -04:00
mix_buffers_no_gain (dst, src, nframes);
/* XXX it would be nice to mark that we wrote into the buffer */
}
}
/* RIGHT OUTPUT */
2020-03-23 11:46:03 -04:00
dst = obufs.get_audio (1).data ();
if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
/* we're moving the pan by an appreciable amount, so we must
2020-03-23 11:46:03 -04:00
* interpolate over 64 samples or nframes, whichever is smaller */
2020-03-23 11:46:03 -04:00
pframes_t const limit = min ((pframes_t)64, nframes);
pframes_t n;
2020-03-23 11:46:03 -04:00
delta = -(delta / (float)(limit));
for (n = 0; n < limit; n++) {
right_interp = right_interp + delta;
2020-03-23 11:46:03 -04:00
right = right_interp + 0.9 * (right - right_interp);
dst[n] += src[n] * right * gain_coeff;
}
/* then pan the rest of the buffer, no need for interpolation for this bit */
pan = right * gain_coeff;
2020-03-23 11:46:03 -04:00
mix_buffers_with_gain (dst + n, src + n, nframes - n, pan);
/* XXX it would be nice to mark the buffer as written to */
} else {
2020-03-23 11:46:03 -04:00
right = desired_right;
right_interp = right;
if ((pan = (right * gain_coeff)) != 1.0f) {
if (pan != 0.0f) {
/* pan is not 1 but also not 0, so we must do it "properly" */
2015-10-05 10:17:49 -04:00
2020-03-23 11:46:03 -04:00
mix_buffers_with_gain (dst, src, nframes, pan);
/* XXX it would be nice to mark the buffer as written to */
}
} else {
/* pan is 1 so we can just copy the input samples straight in */
2015-10-05 10:17:49 -04:00
2020-03-23 11:46:03 -04:00
mix_buffers_no_gain (dst, src, nframes);
/* XXX it would be nice to mark the buffer as written to */
}
}
}
void
Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
samplepos_t start, samplepos_t end, pframes_t nframes,
pan_t** buffers, uint32_t which)
{
2020-03-23 11:46:03 -04:00
assert (obufs.count ().n_audio () == 2);
2020-03-23 11:46:03 -04:00
Sample* dst;
pan_t* pbuf;
Sample* const src = srcbuf.data ();
pan_t* const position = buffers[0];
/* fetch positional data */
if (!_pannable->pan_azimuth_control->list ()->curve ().rt_safe_get_vector (timepos_t (start), timepos_t (end), position, nframes)) {
/* fallback */
2020-03-23 11:46:03 -04:00
distribute_one (srcbuf, obufs, 1.0, nframes, which);
return;
}
/* apply pan law to convert positional data into pan coefficients for
each buffer (output)
*/
#if 0
const float pan_law_attenuation = -3.0f;
2020-03-23 11:46:03 -04:00
const float scale = 2.0f - 4.0f * powf (10.0f, pan_law_attenuation / 20.0f);
#else
float const scale = -0.831783138f;
#endif
for (pframes_t n = 0; n < nframes; ++n) {
2020-03-23 11:46:03 -04:00
float panR = position[n];
const float panL = 1 - panR;
/* note that are overwriting buffers, but its OK
* because we're finished with their old contents
* (position automation data) and are
* replacing it with panning/gain coefficients
* that we need to actually process the data.
*/
buffers[0][n] = panL * (scale * panL + 1.0f - scale);
buffers[1][n] = panR * (scale * panR + 1.0f - scale);
}
/* LEFT OUTPUT */
2020-03-23 11:46:03 -04:00
dst = obufs.get_audio (0).data ();
pbuf = buffers[0];
for (pframes_t n = 0; n < nframes; ++n) {
dst[n] += src[n] * pbuf[n];
}
/* XXX it would be nice to mark the buffer as written to */
/* RIGHT OUTPUT */
2020-03-23 11:46:03 -04:00
dst = obufs.get_audio (1).data ();
pbuf = buffers[1];
for (pframes_t n = 0; n < nframes; ++n) {
dst[n] += src[n] * pbuf[n];
}
/* XXX it would be nice to mark the buffer as written to */
}
Panner*
Panner1in2out::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
{
return new Panner1in2out (p);
}
XMLNode&
Panner1in2out::get_state () const
{
XMLNode& root (Panner::get_state ());
2020-03-23 11:46:03 -04:00
root.set_property (X_ ("uri"), _descriptor.panner_uri);
rework panning -- Squashed commit of the following: commit 6f4f4f161b00cb36252727f67ecc4913eb944fd7 Author: Robin Gareus <robin@gareus.org> Date: Wed Jan 8 22:13:09 2014 +0100 fix panner plugin discovery (prev commit) commit 26e514f4a80af9192cae3cbd62fde0ae95474dfc Author: Robin Gareus <robin@gareus.org> Date: Wed Jan 8 18:56:59 2014 +0100 update panner plugin discovery * recurse dirs in 'PANNER_PATH' and 'panner_dir_name' up to 1 level. * don't look in ardour_dll_directory() -- no panners are supposed to be in there * use .dylib on OSX exclusively. commit a514c3f1c425dccf3d42eee9d2b183b44fd26a03 Author: Robin Gareus <robin@gareus.org> Date: Wed Jan 8 16:48:34 2014 +0100 remove debug/devel printf()s commit d863742ddc69af493ee6a8817bc778968d9b0800 Author: Robin Gareus <robin@gareus.org> Date: Wed Jan 8 16:17:13 2014 +0100 panner-type: session backward/forward compatibility commit 25d5e4c663ada34129451b0f9045ab047d6cc2f0 Author: Robin Gareus <robin@gareus.org> Date: Wed Jan 8 16:09:07 2014 +0100 update URIs -> URLs commit 00a606a43d9456cfbaf43cae4fb598549326ba71 Merge: 0f1cec1 382eb0f Author: Robin Gareus <robin@gareus.org> Date: Wed Jan 8 03:29:45 2014 +0100 Merge branch 'master' into panning commit 0f1cec19babae538c9697eed4be5d6ddc851b013 Author: Robin Gareus <robin@gareus.org> Date: Wed Jan 8 02:41:15 2014 +0100 switch panner ID to URI commit 575282b412c3ae1cd8219cf75f00a1a4239e2813 Author: Robin Gareus <robin@gareus.org> Date: Wed Jan 8 00:50:15 2014 +0100 prepare API for panner URI commit ea62cd049308859782a7bb16e4f18169d8638b46 Author: Robin Gareus <robin@gareus.org> Date: Tue Jan 7 19:57:06 2014 +0100 update development doc relating to panner selection commit 586d7de2392e26b9d7f597b1a00b98dfaa42ecdc Author: Robin Gareus <robin@gareus.org> Date: Tue Jan 7 19:56:24 2014 +0100 clean up PanShell::set_user_selected_panner_type() API commit 99077886a5a1cacece908d87c29c3be12903027e Author: Robin Gareus <robin@gareus.org> Date: Tue Jan 7 04:46:22 2014 +0100 panner bypass: visualize & [in]sensitivity commit 46d688d216f0e67d672376a607157af02b359fb2 Merge: 4e67573 c4cdf61 Author: Robin Gareus <robin@gareus.org> Date: Tue Jan 7 02:18:54 2014 +0100 Merge branch 'master' into panning commit 4e67573517b3d60ddf65729783687b16cfb2adb7 Author: Robin Gareus <robin@gareus.org> Date: Tue Jan 7 01:05:17 2014 +0100 don't call configure_io() for merely swapping panners commit d32a4c51f6967f48f7680554866f1f7b311ccde1 Merge: a3226d4 cec3116 Author: Robin Gareus <robin@gareus.org> Date: Mon Jan 6 23:49:55 2014 +0100 Merge branch 'master' into panning commit a3226d46b598afae54a65ac69320eca84669f347 Author: Robin Gareus <robin@gareus.org> Date: Mon Jan 6 17:52:38 2014 +0100 add notes about panner re-design commit d1ae2366024605f22b05572a81ee249e6fdbcd2f Author: Robin Gareus <robin@gareus.org> Date: Mon Jan 6 15:06:40 2014 +0100 add simple stereo-balance panner for testing commit e0ddd256ff2288b8d8cfad3ad485a916964ce5b5 Author: Robin Gareus <robin@gareus.org> Date: Mon Jan 6 17:02:52 2014 +0100 add frontend/GUI for panner selection commit 2cb8f846755eb5aea8a2620d31ea981c446c4041 Author: Robin Gareus <robin@gareus.org> Date: Mon Jan 6 17:02:20 2014 +0100 prepare backend for panner selection
2014-01-08 18:18:29 -05:00
/* this is needed to allow new sessions to load with old Ardour: */
2020-03-23 11:46:03 -04:00
root.set_property (X_ ("type"), _descriptor.name);
return root;
}
string
Panner1in2out::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
{
2020-03-23 11:46:03 -04:00
double val = ac->get_value ();
switch (ac->parameter ().type ()) {
case PanAzimuthAutomation:
/* We show the position of the center of the image relative to the left & right.
* This is expressed as a pair of percentage values that ranges from (100,0)
* (hard left) through (50,50) (hard center) to (0,100) (hard right).
*
* This is pretty weird, but its the way audio engineers expect it. Just remember that
2020-03-23 11:46:03 -04:00
* the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
*
* This is designed to be as narrow as possible. Dedicated
* panner GUIs can do their own version of this if they need
* something less compact.
*/
return string_compose (_ ("L%1R%2"), (int)rint (100.0 * (1.0 - val)),
(int)rint (100.0 * val));
default:
return _ ("unused");
}
}
void
Panner1in2out::reset ()
{
set_position (0.5);
update ();
}