summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2014-01-10 14:53:03 -0500
committerPaul Davis <paul@linuxaudiosystems.com>2014-01-10 14:53:03 -0500
commitd15fda6d751a465d278f477923075d4783f3b1ca (patch)
tree99e36ff934f50d9c980dbeab09ad021cda3d098c /libs
parent22c303d8f6c355a7229eb459cedf4fbdf0eb3c1d (diff)
parent0bdf4c25cfee8cf2408d2b1367f2c5e5c8c509b1 (diff)
fix merge errors with master
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/ardour/panner.h3
-rw-r--r--libs/ardour/ardour/panner_manager.h3
-rw-r--r--libs/ardour/ardour/panner_shell.h13
-rw-r--r--libs/ardour/ardour/route.h1
-rw-r--r--libs/ardour/panner_manager.cc86
-rw-r--r--libs/ardour/panner_search_path.cc1
-rw-r--r--libs/ardour/panner_shell.cc50
-rw-r--r--libs/ardour/route.cc53
-rw-r--r--libs/audiographer/src/general/sample_format_converter.cc14
-rw-r--r--libs/audiographer/tests/general/sample_format_converter_test.cc18
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/utils.h2
-rw-r--r--libs/gtkmm2ext/utils.cc20
-rw-r--r--libs/panners/1in2out/panner_1in2out.cc5
-rw-r--r--libs/panners/2in2out/panner_2in2out.cc5
-rw-r--r--libs/panners/stereobalance/panner_balance.cc333
-rw-r--r--libs/panners/stereobalance/panner_balance.h83
-rw-r--r--libs/panners/stereobalance/wscript34
-rw-r--r--libs/panners/vbap/vbap.cc7
-rw-r--r--libs/panners/wscript2
19 files changed, 692 insertions, 41 deletions
diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h
index cee59923ef..47b102989b 100644
--- a/libs/ardour/ardour/panner.h
+++ b/libs/ardour/ardour/panner.h
@@ -175,8 +175,11 @@ protected:
extern "C" {
struct LIBARDOUR_API PanPluginDescriptor {
std::string name;
+ std::string panner_uri;
+ std::string gui_uri;
int32_t in;
int32_t out;
+ uint32_t priority;
ARDOUR::Panner* (*factory)(boost::shared_ptr<ARDOUR::Pannable>, boost::shared_ptr<ARDOUR::Speakers>);
};
}
diff --git a/libs/ardour/ardour/panner_manager.h b/libs/ardour/ardour/panner_manager.h
index e47ed36ec3..619beb454f 100644
--- a/libs/ardour/ardour/panner_manager.h
+++ b/libs/ardour/ardour/panner_manager.h
@@ -49,7 +49,8 @@ public:
void discover_panners ();
std::list<PannerInfo*> panner_info;
- PannerInfo* select_panner (ChanCount in, ChanCount out);
+ PannerInfo* select_panner (ChanCount in, ChanCount out, std::string const uri = "");
+ PannerInfo* get_by_uri (std::string uri);
private:
PannerManager();
diff --git a/libs/ardour/ardour/panner_shell.h b/libs/ardour/ardour/panner_shell.h
index b78573212a..76df20489f 100644
--- a/libs/ardour/ardour/panner_shell.h
+++ b/libs/ardour/ardour/panner_shell.h
@@ -37,6 +37,7 @@
namespace ARDOUR {
class Session;
+class Route;
class Panner;
class BufferSet;
class AudioBuffer;
@@ -71,11 +72,23 @@ public:
bool bypassed () const;
void set_bypassed (bool);
+ std::string current_panner_uri() const { return _current_panner_uri; }
+ std::string user_selected_panner_uri() const { return _user_selected_panner_uri; }
+ std::string panner_gui_uri() const { return _panner_gui_uri; }
+
private:
+ friend class Route;
void distribute_no_automation (BufferSet& src, BufferSet& dest, pframes_t nframes, gain_t gain_coeff);
+ bool set_user_selected_panner_uri (std::string const uri);
+
boost::shared_ptr<Panner> _panner;
boost::shared_ptr<Pannable> _pannable;
bool _bypassed;
+
+ std::string _current_panner_uri;
+ std::string _user_selected_panner_uri;
+ std::string _panner_gui_uri;
+ bool _force_reselect;
};
} // namespace ARDOUR
diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h
index fb2b18f177..5cd17ee24e 100644
--- a/libs/ardour/ardour/route.h
+++ b/libs/ardour/ardour/route.h
@@ -256,6 +256,7 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou
int remove_processor (boost::shared_ptr<Processor>, ProcessorStreams* err = 0, bool need_process_lock = true);
int remove_processors (const ProcessorList&, ProcessorStreams* err = 0);
int reorder_processors (const ProcessorList& new_order, ProcessorStreams* err = 0);
+ void set_custom_panner_uri (std::string const panner_uri);
void disable_processors (Placement);
void disable_processors ();
void disable_plugins (Placement);
diff --git a/libs/ardour/panner_manager.cc b/libs/ardour/panner_manager.cc
index 94e8d4ff2f..24fa10e225 100644
--- a/libs/ardour/panner_manager.cc
+++ b/libs/ardour/panner_manager.cc
@@ -24,7 +24,8 @@
#include "pbd/error.h"
#include "pbd/compose.h"
-#include "pbd/file_utils.h"
+#include "pbd/pathscanner.h"
+#include "pbd/stl_delete.h"
#include "ardour/debug.h"
#include "ardour/panner_manager.h"
@@ -59,25 +60,30 @@ PannerManager::instance ()
return *_instance;
}
+static bool panner_filter (const string& str, void */*arg*/)
+{
+#ifdef __APPLE__
+ return str[0] != '.' && (str.length() > 6 && str.find (".dylib") == (str.length() - 6));
+#else
+ return str[0] != '.' && (str.length() > 3 && str.find (".so") == (str.length() - 3));
+#endif
+}
+
void
PannerManager::discover_panners ()
{
- vector<std::string> panner_modules;
-
- Glib::PatternSpec so_extension_pattern("*.so");
- Glib::PatternSpec dylib_extension_pattern("*.dylib");
+ PathScanner scanner;
+ std::vector<std::string *> *panner_modules;
+ std::string search_path = panner_search_path().to_string();
- find_matching_files_in_search_path (panner_search_path (),
- so_extension_pattern, panner_modules);
+ DEBUG_TRACE (DEBUG::Panning, string_compose (_("looking for panners in %1\n"), search_path));
- find_matching_files_in_search_path (panner_search_path (),
- dylib_extension_pattern, panner_modules);
+ panner_modules = scanner (search_path, panner_filter, 0, false, true, 1, true);
- DEBUG_TRACE (DEBUG::Panning, string_compose (_("looking for panners in %1"), panner_search_path().to_string()));
-
- for (vector<std::string>::iterator i = panner_modules.begin(); i != panner_modules.end(); ++i) {
- panner_discover (*i);
+ for (vector<std::string *>::iterator i = panner_modules->begin(); i != panner_modules->end(); ++i) {
+ panner_discover (**i);
}
+ vector_delete (panner_modules);
}
int
@@ -97,7 +103,7 @@ PannerManager::panner_discover (string path)
if (i == panner_info.end()) {
panner_info.push_back (pinfo);
- DEBUG_TRACE (DEBUG::Panning, string_compose(_("Panner discovered: \"%1\" in %2"), pinfo->descriptor.name, path));
+ DEBUG_TRACE (DEBUG::Panning, string_compose(_("Panner discovered: \"%1\" in %2\n"), pinfo->descriptor.name, path));
}
}
@@ -138,53 +144,87 @@ PannerManager::get_descriptor (string path)
}
PannerInfo*
-PannerManager::select_panner (ChanCount in, ChanCount out)
+PannerManager::select_panner (ChanCount in, ChanCount out, std::string const uri)
{
+ PannerInfo* rv = NULL;
PanPluginDescriptor* d;
int32_t nin = in.n_audio();
int32_t nout = out.n_audio();
+ uint32_t priority = 0;
+
+ /* look for user-preference -- check if channels match */
+ for (list<PannerInfo*>::iterator p = panner_info.begin(); p != panner_info.end(); ++p) {
+ d = &(*p)->descriptor;
+ if (d->panner_uri != uri) continue;
+ if (d->in != nin && d->in != -1) continue;
+ if (d->out != nout && d->out != -1) continue;
+ return *p;
+ }
/* look for exact match first */
for (list<PannerInfo*>::iterator p = panner_info.begin(); p != panner_info.end(); ++p) {
d = &(*p)->descriptor;
- if (d->in == nin && d->out == nout) {
- return *p;
+ if (d->in == nin && d->out == nout && d->priority > priority) {
+ priority = d->priority;
+ rv = *p;
}
}
+ if (rv) { return rv; }
/* no exact match, look for good fit on inputs and variable on outputs */
+ priority = 0;
for (list<PannerInfo*>::iterator p = panner_info.begin(); p != panner_info.end(); ++p) {
d = &(*p)->descriptor;
- if (d->in == nin && d->out == -1) {
- return *p;
+ if (d->in == nin && d->out == -1 && d->priority > priority) {
+ priority = d->priority;
+ rv = *p;
}
}
+ if (rv) { return rv; }
/* no exact match, look for good fit on outputs and variable on inputs */
+ priority = 0;
for (list<PannerInfo*>::iterator p = panner_info.begin(); p != panner_info.end(); ++p) {
d = &(*p)->descriptor;
- if (d->in == -1 && d->out == nout) {
- return *p;
+ if (d->in == -1 && d->out == nout && d->priority > priority) {
+ priority = d->priority;
+ rv = *p;
}
}
+ if (rv) { return rv; }
/* no exact match, look for variable fit on inputs and outputs */
+ priority = 0;
for (list<PannerInfo*>::iterator p = panner_info.begin(); p != panner_info.end(); ++p) {
d = &(*p)->descriptor;
- if (d->in == -1 && d->out == -1) {
- return *p;
+ if (d->in == -1 && d->out == -1 && d->priority > priority) {
+ priority = d->priority;
+ rv = *p;
}
}
+ if (rv) { return rv; }
warning << string_compose (_("no panner discovered for in/out = %1/%2"), nin, nout) << endmsg;
return 0;
}
+
+PannerInfo*
+PannerManager::get_by_uri (std::string uri)
+{
+ PannerInfo* pi = NULL;
+ for (list<PannerInfo*>::iterator p = panner_info.begin(); p != panner_info.end(); ++p) {
+ if ((*p)->descriptor.panner_uri != uri) continue;
+ pi = (*p);
+ break;
+ }
+ return pi;
+}
diff --git a/libs/ardour/panner_search_path.cc b/libs/ardour/panner_search_path.cc
index 63802ffc4e..49349238e4 100644
--- a/libs/ardour/panner_search_path.cc
+++ b/libs/ardour/panner_search_path.cc
@@ -37,7 +37,6 @@ panner_search_path ()
SearchPath spath(user_config_directory ());
spath += ardour_dll_directory ();
spath.add_subdirectory_to_paths(panner_dir_name);
-
spath += SearchPath(Glib::getenv(panner_env_variable_name));
return spath;
}
diff --git a/libs/ardour/panner_shell.cc b/libs/ardour/panner_shell.cc
index 1aeb94eb7e..a25cb49ab5 100644
--- a/libs/ardour/panner_shell.cc
+++ b/libs/ardour/panner_shell.cc
@@ -63,6 +63,10 @@ PannerShell::PannerShell (string name, Session& s, boost::shared_ptr<Pannable> p
: SessionObject (s, name)
, _pannable (p)
, _bypassed (false)
+ , _current_panner_uri("")
+ , _user_selected_panner_uri("")
+ , _panner_gui_uri("")
+ , _force_reselect (false)
{
set_name (name);
}
@@ -82,7 +86,7 @@ PannerShell::configure_io (ChanCount in, ChanCount out)
the config hasn't changed, we're done.
*/
- if (_panner && (_panner->in().n_audio() == nins) && (_panner->out().n_audio() == nouts)) {
+ if (!_force_reselect && _panner && (_panner->in().n_audio() == nins) && (_panner->out().n_audio() == nouts)) {
return;
}
@@ -90,17 +94,21 @@ PannerShell::configure_io (ChanCount in, ChanCount out)
/* no need for panning with less than 2 outputs or no inputs */
if (_panner) {
_panner.reset ();
+ _current_panner_uri = "";
+ _panner_gui_uri = "";
Changed (); /* EMIT SIGNAL */
}
return;
}
- PannerInfo* pi = PannerManager::instance().select_panner (in, out);
+ PannerInfo* pi = PannerManager::instance().select_panner (in, out, _user_selected_panner_uri);
if (!pi) {
cerr << "No panner found: check that panners are being discovered correctly during startup.\n";
assert (pi);
}
+ DEBUG_TRACE (DEBUG::Panning, string_compose (_("select panner: %1\n"), pi->descriptor.name.c_str()));
+
boost::shared_ptr<Speakers> speakers = _session.get_speakers ();
if (nouts != speakers->size()) {
@@ -116,6 +124,8 @@ PannerShell::configure_io (ChanCount in, ChanCount out)
// boost_debug_shared_ptr_mark_interesting (p, "Panner");
_panner.reset (p);
_panner->configure_io (in, out);
+ _current_panner_uri = pi->descriptor.panner_uri;
+ _panner_gui_uri = pi->descriptor.gui_uri;
Changed (); /* EMIT SIGNAL */
}
@@ -126,6 +136,7 @@ PannerShell::get_state ()
XMLNode* node = new XMLNode ("PannerShell");
node->add_property (X_("bypassed"), _bypassed ? X_("yes") : X_("no"));
+ node->add_property (X_("user-panner"), _user_selected_panner_uri);
if (_panner) {
node->add_child_nocopy (_panner->get_state ());
@@ -146,12 +157,29 @@ PannerShell::set_state (const XMLNode& node, int version)
set_bypassed (string_is_affirmative (prop->value ()));
}
+ if ((prop = node.property (X_("user-panner"))) != 0) {
+ _user_selected_panner_uri = prop->value ();
+ }
+
_panner.reset ();
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
if ((*niter)->name() == X_("Panner")) {
+ if ((prop = (*niter)->property (X_("uri")))) {
+ PannerInfo* p = PannerManager::instance().get_by_uri(prop->value());
+ if (p) {
+ _panner.reset (p->descriptor.factory (_pannable, _session.get_speakers ()));
+ _current_panner_uri = p->descriptor.panner_uri;
+ _panner_gui_uri = p->descriptor.gui_uri;
+ if (_panner->set_state (**niter, version) == 0) {
+ return -1;
+ }
+ }
+ }
+
+ else /* backwards compatibility */
if ((prop = (*niter)->property (X_("type")))) {
list<PannerInfo*>::iterator p;
@@ -166,6 +194,8 @@ PannerShell::set_state (const XMLNode& node, int version)
*/
_panner.reset ((*p)->descriptor.factory (_pannable, _session.get_speakers ()));
+ _current_panner_uri = (*p)->descriptor.panner_uri;
+ _panner_gui_uri = (*p)->descriptor.gui_uri;
if (_panner->set_state (**niter, version) == 0) {
return -1;
@@ -347,3 +377,19 @@ PannerShell::bypassed () const
{
return _bypassed;
}
+
+/* set custom-panner config
+ *
+ * This function is intended to be only called from
+ * Route::set_custom_panner()
+ * which will trigger IO-reconfigutaion if this fn return true
+ */
+bool
+PannerShell::set_user_selected_panner_uri (std::string const uri)
+{
+ if (uri == _user_selected_panner_uri) return false;
+ _user_selected_panner_uri = uri;
+ if (uri == _current_panner_uri) return false;
+ _force_reselect = true;
+ return true;
+}
diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc
index c032b77eff..6f23e920d4 100644
--- a/libs/ardour/route.cc
+++ b/libs/ardour/route.cc
@@ -51,6 +51,7 @@
#include "ardour/midi_port.h"
#include "ardour/monitor_processor.h"
#include "ardour/pannable.h"
+#include "ardour/panner.h"
#include "ardour/panner_shell.h"
#include "ardour/plugin_insert.h"
#include "ardour/port.h"
@@ -1574,6 +1575,58 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
}
void
+Route::set_custom_panner_uri (std::string const panner_uri)
+{
+ if (_in_configure_processors) {
+ DEBUG_TRACE (DEBUG::Panning, string_compose (_("Route::set_custom_panner_uri '%1' -- called while in_configure_processors\n"), name()));
+ return;
+ }
+
+ if (!_main_outs->panner_shell()->set_user_selected_panner_uri(panner_uri)) {
+ DEBUG_TRACE (DEBUG::Panning, string_compose (_("Route::set_custom_panner_uri '%1 '%2' -- no change needed\n"), name(), panner_uri));
+ /* no change needed */
+ return;
+ }
+
+ DEBUG_TRACE (DEBUG::Panning, string_compose (_("Route::set_custom_panner_uri '%1 '%2' -- reconfigure I/O\n"), name(), panner_uri));
+
+ /* reconfigure I/O -- re-initialize panner modules */
+ {
+ Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+ Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+
+ for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) {
+ boost::shared_ptr<Delivery> dl;
+ boost::shared_ptr<Panner> panner;
+ if ((dl = boost::dynamic_pointer_cast<Delivery> (*p)) == 0) {
+ continue;
+ }
+ if (!dl->panner_shell()) {
+ continue;
+ }
+ if (!(panner = dl->panner_shell()->panner())) {
+ continue;
+ }
+ /* _main_outs has already been set before the loop.
+ * Ignore the return status here. It need reconfiguration */
+ if (dl->panner_shell() != _main_outs->panner_shell()) {
+ if (!dl->panner_shell()->set_user_selected_panner_uri(panner_uri)) {
+ continue;
+ }
+ }
+
+ ChanCount in = panner->in();
+ ChanCount out = panner->out();
+ dl->panner_shell()->configure_io(in, out);
+ dl->panner_shell()->pannable()->set_panner(dl->panner_shell()->panner());
+ }
+ }
+
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+ _session.set_dirty ();
+}
+
+void
Route::reset_instrument_info ()
{
boost::shared_ptr<Processor> instr = the_instrument();
diff --git a/libs/audiographer/src/general/sample_format_converter.cc b/libs/audiographer/src/general/sample_format_converter.cc
index 5fe9a1185b..aaeda09477 100644
--- a/libs/audiographer/src/general/sample_format_converter.cc
+++ b/libs/audiographer/src/general/sample_format_converter.cc
@@ -54,10 +54,14 @@ template <>
void
SampleFormatConverter<int32_t>::init (framecnt_t max_frames, int type, int data_width)
{
- // GDither is broken with GDither32bit if the dither depth is bigger than 24
- if(throw_level (ThrowObject) && data_width > 24) {
- throw Exception (*this, "Trying to use SampleFormatConverter<int32_t> a data width > 24");
+ if(throw_level (ThrowObject) && data_width > 32) {
+ throw Exception (*this, "Trying to use SampleFormatConverter<int32_t> with a data width > 32");
}
+
+ // GDither is broken with GDither32bit if the dither depth is bigger than 24.
+ // And since floats only have 24 bits of data, we are fine with this.
+ data_width = std::min(data_width, 24);
+
init_common (max_frames);
dither = gdither_new ((GDitherType) type, channels, GDither32bit, data_width);
}
@@ -68,7 +72,7 @@ SampleFormatConverter<int16_t>::init (framecnt_t max_frames, int type, int data_
{
if (throw_level (ThrowObject) && data_width > 16) {
throw Exception (*this, boost::str(boost::format
- ("Data width (%1) too large for int16_t")
+ ("Data width (%1%) too large for int16_t")
% data_width));
}
init_common (max_frames);
@@ -81,7 +85,7 @@ SampleFormatConverter<uint8_t>::init (framecnt_t max_frames, int type, int data_
{
if (throw_level (ThrowObject) && data_width > 8) {
throw Exception (*this, boost::str(boost::format
- ("Data width (%1) too large for uint8_t")
+ ("Data width (%1%) too large for uint8_t")
% data_width));
}
init_common (max_frames);
diff --git a/libs/audiographer/tests/general/sample_format_converter_test.cc b/libs/audiographer/tests/general/sample_format_converter_test.cc
index 9239564d5b..977e2b390a 100644
--- a/libs/audiographer/tests/general/sample_format_converter_test.cc
+++ b/libs/audiographer/tests/general/sample_format_converter_test.cc
@@ -31,27 +31,31 @@ class SampleFormatConverterTest : public CppUnit::TestFixture
void testInit()
{
+ // Float never uses dithering and should always use full 32 bits of data
boost::shared_ptr<SampleFormatConverter<float> > f_converter (new SampleFormatConverter<float>(1));
f_converter->init (frames, D_Tri, 32); // Doesn't throw
CPPUNIT_ASSERT_THROW (f_converter->init (frames, D_Tri, 24), Exception);
CPPUNIT_ASSERT_THROW (f_converter->init (frames, D_Tri, 48), Exception);
-
+
+ /* Test that too large data widths throw.
+ We are fine with unnecessarily narrow data widths */
+
boost::shared_ptr<SampleFormatConverter<int32_t> > i_converter (new SampleFormatConverter<int32_t>(1));
i_converter->init (frames, D_Tri, 32); // Doesn't throw
i_converter->init (frames, D_Tri, 24); // Doesn't throw
- CPPUNIT_ASSERT_THROW (i_converter->init (frames, D_Tri, 8), Exception);
- CPPUNIT_ASSERT_THROW (i_converter->init (frames, D_Tri, 16), Exception);
+ i_converter->init (frames, D_Tri, 8); // Doesn't throw
+ i_converter->init (frames, D_Tri, 16); // Doesn't throw
CPPUNIT_ASSERT_THROW (i_converter->init (frames, D_Tri, 48), Exception);
-
+
boost::shared_ptr<SampleFormatConverter<int16_t> > i16_converter (new SampleFormatConverter<int16_t>(1));
i16_converter->init (frames, D_Tri, 16); // Doesn't throw
- CPPUNIT_ASSERT_THROW (i16_converter->init (frames, D_Tri, 8), Exception);
+ i16_converter->init (frames, D_Tri, 8); // Doesn't throw
CPPUNIT_ASSERT_THROW (i16_converter->init (frames, D_Tri, 32), Exception);
CPPUNIT_ASSERT_THROW (i16_converter->init (frames, D_Tri, 48), Exception);
-
+
boost::shared_ptr<SampleFormatConverter<uint8_t> > ui_converter (new SampleFormatConverter<uint8_t>(1));
ui_converter->init (frames, D_Tri, 8); // Doesn't throw
- CPPUNIT_ASSERT_THROW (ui_converter->init (frames, D_Tri, 4), Exception);
+ ui_converter->init (frames, D_Tri, 4); // Doesn't throw
CPPUNIT_ASSERT_THROW (ui_converter->init (frames, D_Tri, 16), Exception);
}
diff --git a/libs/gtkmm2ext/gtkmm2ext/utils.h b/libs/gtkmm2ext/gtkmm2ext/utils.h
index 112ca42e77..de9f7b765d 100644
--- a/libs/gtkmm2ext/gtkmm2ext/utils.h
+++ b/libs/gtkmm2ext/gtkmm2ext/utils.h
@@ -108,6 +108,7 @@ namespace Gtkmm2ext {
LIBGTKMM2EXT_API void rounded_top_half_rectangle (Cairo::RefPtr<Cairo::Context>, double x, double y, double w, double h, double r=10);
LIBGTKMM2EXT_API void rounded_bottom_half_rectangle (Cairo::RefPtr<Cairo::Context>, double x, double y, double w, double h, double r=10);
LIBGTKMM2EXT_API void rounded_right_half_rectangle (Cairo::RefPtr<Cairo::Context>, double x, double y, double w, double h, double r=10);
+ LIBGTKMM2EXT_API void rounded_left_half_rectangle (Cairo::RefPtr<Cairo::Context>, double x, double y, double w, double h, double r=10);
/* C API for rounded rectangles */
@@ -118,6 +119,7 @@ namespace Gtkmm2ext {
LIBGTKMM2EXT_API void rounded_top_half_rectangle (cairo_t*, double x, double y, double w, double h, double r=10);
LIBGTKMM2EXT_API void rounded_bottom_half_rectangle (cairo_t*, double x, double y, double w, double h, double r=10);
LIBGTKMM2EXT_API void rounded_right_half_rectangle (cairo_t*, double x, double y, double w, double h, double r=10);
+ LIBGTKMM2EXT_API void rounded_left_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r=10);
LIBGTKMM2EXT_API Gtk::Label* left_aligned_label (std::string const &);
diff --git a/libs/gtkmm2ext/utils.cc b/libs/gtkmm2ext/utils.cc
index 77ed843896..0f04b8cfe7 100644
--- a/libs/gtkmm2ext/utils.cc
+++ b/libs/gtkmm2ext/utils.cc
@@ -415,6 +415,13 @@ Gtkmm2ext::rounded_bottom_half_rectangle (Cairo::RefPtr<Cairo::Context> context,
{
rounded_bottom_half_rectangle (context->cobj(), x, y, w, h, r);
}
+
+void
+Gtkmm2ext::rounded_left_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
+{
+ rounded_left_half_rectangle (context->cobj(), x, y, w, h, r);
+}
+
void
Gtkmm2ext::rounded_right_half_rectangle (Cairo::RefPtr<Cairo::Context> context, double x, double y, double w, double h, double r)
{
@@ -435,6 +442,19 @@ Gtkmm2ext::rounded_rectangle (cairo_t* cr, double x, double y, double w, double
}
void
+Gtkmm2ext::rounded_left_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
+{
+ double degrees = M_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_line_to (cr, x+w, y); // tr
+ cairo_line_to (cr, x+w, y + h); // br
+ cairo_arc (cr, x + r, y + h - r, r, 90 * degrees, 180 * degrees); //bl
+ cairo_arc (cr, x + r, y + r, r, 180 * degrees, 270 * degrees); //tl
+ cairo_close_path (cr);
+}
+
+void
Gtkmm2ext::rounded_right_half_rectangle (cairo_t* cr, double x, double y, double w, double h, double r)
{
double degrees = M_PI / 180.0;
diff --git a/libs/panners/1in2out/panner_1in2out.cc b/libs/panners/1in2out/panner_1in2out.cc
index 4524ed560b..12e7896cfb 100644
--- a/libs/panners/1in2out/panner_1in2out.cc
+++ b/libs/panners/1in2out/panner_1in2out.cc
@@ -63,7 +63,10 @@ using namespace PBD;
static PanPluginDescriptor _descriptor = {
"Mono to Stereo Panner",
+ "http://ardour.org/plugin/panner_1in2out",
+ "http://ardour.org/plugin/panner_1in2out#ui",
1, 2,
+ 10000,
Panner1in2out::factory
};
@@ -332,6 +335,8 @@ XMLNode&
Panner1in2out::get_state ()
{
XMLNode& root (Panner::get_state ());
+ root.add_property (X_("uri"), _descriptor.panner_uri);
+ /* this is needed to allow new sessions to load with old Ardour: */
root.add_property (X_("type"), _descriptor.name);
return root;
}
diff --git a/libs/panners/2in2out/panner_2in2out.cc b/libs/panners/2in2out/panner_2in2out.cc
index a316b764c7..f801a36ff4 100644
--- a/libs/panners/2in2out/panner_2in2out.cc
+++ b/libs/panners/2in2out/panner_2in2out.cc
@@ -63,7 +63,10 @@ using namespace PBD;
static PanPluginDescriptor _descriptor = {
"Equal Power Stereo",
+ "http://ardour.org/plugin/panner_2in2out",
+ "http://ardour.org/plugin/panner_2in2out#ui",
2, 2,
+ 10000,
Panner2in2out::factory
};
@@ -464,6 +467,8 @@ XMLNode&
Panner2in2out::get_state ()
{
XMLNode& root (Panner::get_state ());
+ root.add_property (X_("uri"), _descriptor.panner_uri);
+ /* this is needed to allow new sessions to load with old Ardour: */
root.add_property (X_("type"), _descriptor.name);
return root;
}
diff --git a/libs/panners/stereobalance/panner_balance.cc b/libs/panners/stereobalance/panner_balance.cc
new file mode 100644
index 0000000000..d5ab96b73c
--- /dev/null
+++ b/libs/panners/stereobalance/panner_balance.cc
@@ -0,0 +1,333 @@
+/*
+ Copyright (C) 2004-2011 Paul Davis
+ adopted from 2in2out panner by 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <inttypes.h>
+
+#include <cmath>
+#include <cerrno>
+#include <fstream>
+#include <cstdlib>
+#include <string>
+#include <cstdio>
+#include <locale.h>
+#include <unistd.h>
+#include <float.h>
+#include <iomanip>
+
+#include <glibmm.h>
+
+#include "pbd/cartesian.h"
+#include "pbd/convert.h"
+#include "pbd/error.h"
+#include "pbd/failed_constructor.h"
+#include "pbd/xml++.h"
+#include "pbd/enumwriter.h"
+
+#include "evoral/Curve.hpp"
+
+#include "ardour/audio_buffer.h"
+#include "ardour/audio_buffer.h"
+#include "ardour/buffer_set.h"
+#include "ardour/pan_controllable.h"
+#include "ardour/pannable.h"
+#include "ardour/runtime_functions.h"
+#include "ardour/session.h"
+#include "ardour/utils.h"
+#include "ardour/mix.h"
+
+#include "panner_balance.h"
+
+#include "i18n.h"
+
+#include "pbd/mathfix.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+static PanPluginDescriptor _descriptor = {
+ "Stereo Balance",
+ "http://ardour.org/plugin/panner_balance",
+ "http://ardour.org/plugin/panner_balance#ui",
+ 2, 2,
+ 2000,
+ Pannerbalance::factory
+};
+
+extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
+
+Pannerbalance::Pannerbalance (boost::shared_ptr<Pannable> p)
+ : Panner (p)
+{
+ if (!_pannable->has_state()) {
+ _pannable->pan_azimuth_control->set_value (0.5);
+ }
+
+ update ();
+
+ /* LEFT SIGNAL */
+ pos_interp[0] = pos[0] = desired_pos[0];
+ /* RIGHT SIGNAL */
+ pos_interp[1] = pos[1] = desired_pos[1];
+
+ _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Pannerbalance::update, this));
+}
+
+Pannerbalance::~Pannerbalance ()
+{
+}
+
+double
+Pannerbalance::position () const
+{
+ return _pannable->pan_azimuth_control->get_value();
+}
+
+ void
+Pannerbalance::set_position (double p)
+{
+ if (clamp_position (p)) {
+ _pannable->pan_azimuth_control->set_value (p);
+ }
+}
+
+ void
+Pannerbalance::thaw ()
+{
+ Panner::thaw ();
+ if (_frozen == 0) {
+ update ();
+ }
+}
+
+void
+Pannerbalance::update ()
+{
+ if (_frozen) {
+ return;
+ }
+
+ float const pos = _pannable->pan_azimuth_control->get_value();
+
+ if (pos == .5) {
+ desired_pos[0] = 1.0;
+ desired_pos[1] = 1.0;
+ } else if (pos > .5) {
+ desired_pos[0] = 2 - 2. * pos;
+ desired_pos[1] = 1.0;
+ } else {
+ desired_pos[0] = 1.0;
+ desired_pos[1] = 2. * pos;
+ }
+}
+
+bool
+Pannerbalance::clamp_position (double& p)
+{
+ p = max (min (p, 1.0), 0.0);
+ return true;
+}
+
+pair<double, double>
+Pannerbalance::position_range () const
+{
+ return make_pair (0, 1);
+}
+
+void
+Pannerbalance::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which)
+{
+ assert (obufs.count().n_audio() == 2);
+
+ pan_t delta;
+ Sample* dst;
+ pan_t pan;
+
+ Sample* const src = srcbuf.data();
+
+ dst = obufs.get_audio(which).data();
+
+ if (fabsf ((delta = (pos[which] - desired_pos[which]))) > 0.002) { // about 1 degree of arc
+
+ /* we've moving the pan by an appreciable amount, so we must
+ interpolate over 64 frames or nframes, whichever is smaller */
+
+ pframes_t const limit = min ((pframes_t) 64, nframes);
+ pframes_t n;
+
+ delta = -(delta / (float) (limit));
+
+ for (n = 0; n < limit; n++) {
+ pos_interp[which] = pos_interp[which] + delta;
+ pos[which] = pos_interp[which] + 0.9 * (pos[which] - pos_interp[which]);
+ dst[n] += src[n] * pos[which] * gain_coeff;
+ }
+
+ /* then pan the rest of the buffer; no need for interpolation for this bit */
+
+ pan = pos[which] * gain_coeff;
+
+ mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
+
+ } else {
+
+ pos[which] = desired_pos[which];
+ pos_interp[which] = pos[which];
+
+ if ((pan = (pos[which] * gain_coeff)) != 1.0f) {
+
+ if (pan != 0.0f) {
+
+ /* pan is 1 but also not 0, so we must do it "properly" */
+
+ //obufs.get_audio(1).read_from (srcbuf, nframes);
+ mix_buffers_with_gain(dst,src,nframes,pan);
+
+ /* mark that we wrote into the buffer */
+
+ // obufs[0] = 0;
+
+ }
+
+ } else {
+ /* pan is 1 so we can just copy the input samples straight in */
+ mix_buffers_no_gain(dst,src,nframes);
+ }
+ }
+}
+
+void
+Pannerbalance::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
+ framepos_t start, framepos_t end, pframes_t nframes,
+ pan_t** buffers, uint32_t which)
+{
+ assert (obufs.count().n_audio() == 2);
+
+ 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 (start, end, position, nframes)) {
+ /* fallback */
+ distribute_one (srcbuf, obufs, 1.0, nframes, which);
+ return;
+ }
+
+ for (pframes_t n = 0; n < nframes; ++n) {
+
+ float const pos = position[n];
+
+ if (which == 0) { // Left
+ if (pos > .5) {
+ buffers[which][n] = 2 - 2. * pos;
+ } else {
+ buffers[which][n] = 1.0;
+ }
+ } else { // Right
+ if (pos < .5) {
+ buffers[which][n] = 2. * pos;
+ } else {
+ buffers[which][n] = 1.0;
+ }
+ }
+ }
+
+ dst = obufs.get_audio(which).data();
+ pbuf = buffers[which];
+
+ 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*
+Pannerbalance::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
+{
+ return new Pannerbalance (p);
+}
+
+ XMLNode&
+Pannerbalance::get_state ()
+{
+ XMLNode& root (Panner::get_state ());
+ root.add_property (X_("uri"), _descriptor.panner_uri);
+ /* this is needed to allow new sessions to load with old Ardour: */
+ root.add_property (X_("type"), _descriptor.name);
+ return root;
+}
+
+std::set<Evoral::Parameter>
+Pannerbalance::what_can_be_automated() const
+{
+ set<Evoral::Parameter> s;
+ s.insert (Evoral::Parameter (PanAzimuthAutomation));
+ return s;
+}
+
+string
+Pannerbalance::describe_parameter (Evoral::Parameter p)
+{
+ switch (p.type()) {
+ case PanAzimuthAutomation:
+ return _("L/R");
+ default:
+ return _pannable->describe_parameter (p);
+ }
+}
+
+string
+Pannerbalance::value_as_string (boost::shared_ptr<AutomationControl> ac) const
+{
+ /* DO NOT USE LocaleGuard HERE */
+ 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 wierd, but its the way audio engineers expect it. Just remember that
+ 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 _pannable->value_as_string (ac);
+ }
+}
+
+void
+Pannerbalance::reset ()
+{
+ set_position (0.5);
+ update ();
+}
diff --git a/libs/panners/stereobalance/panner_balance.h b/libs/panners/stereobalance/panner_balance.h
new file mode 100644
index 0000000000..f381340888
--- /dev/null
+++ b/libs/panners/stereobalance/panner_balance.h
@@ -0,0 +1,83 @@
+/*
+ Copyright (C) 2004-2014 Paul Davis
+ adopted from 2in2out panner by 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __ardour_panner_balance_h__
+#define __ardour_panner_balance_h__
+
+#include <cmath>
+#include <cassert>
+#include <vector>
+#include <string>
+#include <iostream>
+
+#include "pbd/stateful.h"
+#include "pbd/controllable.h"
+#include "pbd/cartesian.h"
+
+#include "ardour/automation_control.h"
+#include "ardour/automatable.h"
+#include "ardour/panner.h"
+#include "ardour/types.h"
+
+namespace ARDOUR {
+
+class Pannerbalance : public Panner
+{
+ public:
+ Pannerbalance (boost::shared_ptr<Pannable>);
+ ~Pannerbalance ();
+
+ ChanCount in() const { return ChanCount (DataType::AUDIO, 2); }
+ ChanCount out() const { return ChanCount (DataType::AUDIO, 2); }
+
+ void set_position (double);
+ bool clamp_position (double&);
+ std::pair<double, double> position_range () const;
+ double position () const;
+
+ std::set<Evoral::Parameter> what_can_be_automated() const;
+
+ static Panner* factory (boost::shared_ptr<Pannable>, boost::shared_ptr<Speakers>);
+
+ std::string describe_parameter (Evoral::Parameter);
+ std::string value_as_string (boost::shared_ptr<AutomationControl>) const;
+
+ XMLNode& get_state ();
+
+ void reset ();
+ void thaw ();
+
+ protected:
+ float pos[2];
+ float desired_pos[2];
+ float pos_interp[2];
+
+ void update ();
+
+ private:
+ void distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which);
+ void distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
+ framepos_t start, framepos_t end, pframes_t nframes,
+ pan_t** buffers, uint32_t which);
+};
+
+} // namespace
+
+#endif /* __ardour_panner_balance_h__ */
diff --git a/libs/panners/stereobalance/wscript b/libs/panners/stereobalance/wscript
new file mode 100644
index 0000000000..75eccca419
--- /dev/null
+++ b/libs/panners/stereobalance/wscript
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+import os
+
+# Library version (UNIX style major, minor, micro)
+# major increment <=> incompatible changes
+# minor increment <=> compatible changes (additions)
+# micro increment <=> no interface changes
+LIBARDOUR_PAN2IN2OUT_LIB_VERSION = '1.0.0'
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+ autowaf.set_options(opt)
+
+def configure(conf):
+ autowaf.configure(conf)
+
+def build(bld):
+ obj = bld(features = 'cxx cxxshlib')
+ obj.source = [ 'panner_balance.cc' ]
+ obj.export_includes = ['.']
+ obj.cxxflags = '-DPACKAGE="libardour_panbalance"'
+ obj.includes = ['.']
+ obj.name = 'libardour_panbalance'
+ obj.target = 'panbalance'
+ obj.use = 'libardour libardour_cp libpbd'
+ obj.vnum = LIBARDOUR_PAN2IN2OUT_LIB_VERSION
+ obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'panners')
+
+def shutdown():
+ autowaf.shutdown()
diff --git a/libs/panners/vbap/vbap.cc b/libs/panners/vbap/vbap.cc
index 1cef98fc7d..2241ab96e3 100644
--- a/libs/panners/vbap/vbap.cc
+++ b/libs/panners/vbap/vbap.cc
@@ -46,7 +46,10 @@ using namespace std;
static PanPluginDescriptor _descriptor = {
"VBAP 2D panner",
+ "http://ardour.org/plugin/panner_vbap",
+ "http://ardour.org/plugin/panner_vbap#ui",
-1, -1,
+ 1000,
VBAPanner::factory
};
@@ -392,7 +395,9 @@ VBAPanner::distribute_one_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/,
XMLNode&
VBAPanner::get_state ()
{
- XMLNode& node (Panner::get_state());
+ XMLNode& node (Panner::get_state());
+ node.add_property (X_("uri"), _descriptor.panner_uri);
+ /* this is needed to allow new sessions to load with old Ardour: */
node.add_property (X_("type"), _descriptor.name);
return node;
}
diff --git a/libs/panners/wscript b/libs/panners/wscript
index aec57eb40f..f3ce6e6f19 100644
--- a/libs/panners/wscript
+++ b/libs/panners/wscript
@@ -6,7 +6,7 @@ import os
top = '.'
out = 'build'
-panners = [ '2in2out', '1in2out', 'vbap' ]
+panners = [ '2in2out', '1in2out', 'vbap', 'stereobalance' ]
def options(opt):
autowaf.set_options(opt)