summaryrefslogtreecommitdiff
path: root/libs/vamp-plugins
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2016-02-11 10:24:22 +0100
committerRobin Gareus <robin@gareus.org>2016-02-11 14:14:01 +0100
commitd2fa4b338859e8e001ade219161b83c9bfa7130b (patch)
tree15043f5fdba8103d6312a19583bcbd18ed08296b /libs/vamp-plugins
parent148f2ab8e560fa5f794c5f578b7e5fdb9d568157 (diff)
add dBTP Vamp plugin
Diffstat (limited to 'libs/vamp-plugins')
-rw-r--r--libs/vamp-plugins/TruePeak.cpp591
-rw-r--r--libs/vamp-plugins/TruePeak.h174
-rw-r--r--libs/vamp-plugins/plugins.cpp6
-rw-r--r--libs/vamp-plugins/wscript1
4 files changed, 770 insertions, 2 deletions
diff --git a/libs/vamp-plugins/TruePeak.cpp b/libs/vamp-plugins/TruePeak.cpp
new file mode 100644
index 0000000000..3e2ba74004
--- /dev/null
+++ b/libs/vamp-plugins/TruePeak.cpp
@@ -0,0 +1,591 @@
+/*
+ * Copyright (C) 2013-2016 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2006-2012 Fons Adriaensen <fons@linuxaudio.org>
+ * Copyright (C) 2006 Chris Cannam
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include "TruePeak.h"
+
+namespace TruePeakMeter {
+
+static double sinc (double x)
+{
+ x = fabs (x);
+ if (x < 1e-6) return 1.0;
+ x *= M_PI;
+ return sin (x) / x;
+}
+
+static double wind (double x)
+{
+ x = fabs (x);
+ if (x >= 1.0) return 0.0f;
+ x *= M_PI;
+ return 0.384 + 0.500 * cos (x) + 0.116 * cos (2 * x);
+}
+
+Resampler_table *Resampler_table::_list = 0;
+Resampler_mutex Resampler_table::_mutex;
+
+Resampler_table::Resampler_table (double fr, unsigned int hl, unsigned int np)
+ : _next (0)
+ , _refc (0)
+ , _fr (fr)
+ , _hl (hl)
+ , _np (np)
+{
+ unsigned int i, j;
+ double t;
+ float *p;
+
+ _ctab = new float [hl * (np + 1)];
+ p = _ctab;
+ for (j = 0; j <= np; j++)
+ {
+ t = (double) j / (double) np;
+ for (i = 0; i < hl; i++)
+ {
+ p [hl - i - 1] = (float)(fr * sinc (t * fr) * wind (t / hl));
+ t += 1;
+ }
+ p += hl;
+ }
+}
+
+Resampler_table::~Resampler_table (void)
+{
+ delete[] _ctab;
+}
+
+Resampler_table *
+Resampler_table::create (double fr, unsigned int hl, unsigned int np)
+{
+ Resampler_table *P;
+
+ _mutex.lock ();
+ P = _list;
+ while (P)
+ {
+ if ((fr >= P->_fr * 0.999) && (fr <= P->_fr * 1.001) && (hl == P->_hl) && (np == P->_np))
+ {
+ P->_refc++;
+ _mutex.unlock ();
+ return P;
+ }
+ P = P->_next;
+ }
+ P = new Resampler_table (fr, hl, np);
+ P->_refc = 1;
+ P->_next = _list;
+ _list = P;
+ _mutex.unlock ();
+ return P;
+}
+
+void
+Resampler_table::destroy (Resampler_table *T)
+{
+ Resampler_table *P, *Q;
+
+ _mutex.lock ();
+ if (T)
+ {
+ T->_refc--;
+ if (T->_refc == 0)
+ {
+ P = _list;
+ Q = 0;
+ while (P)
+ {
+ if (P == T)
+ {
+ if (Q) Q->_next = T->_next;
+ else _list = T->_next;
+ break;
+ }
+ Q = P;
+ P = P->_next;
+ }
+ delete T;
+ }
+ }
+ _mutex.unlock ();
+}
+
+static unsigned int
+gcd (unsigned int a, unsigned int b)
+{
+ if (a == 0) return b;
+ if (b == 0) return a;
+ while (1)
+ {
+ if (a > b)
+ {
+ a = a % b;
+ if (a == 0) return b;
+ if (a == 1) return 1;
+ }
+ else
+ {
+ b = b % a;
+ if (b == 0) return a;
+ if (b == 1) return 1;
+ }
+ }
+ return 1;
+}
+
+Resampler::Resampler (void)
+ : _table (0)
+ , _nchan (0)
+ , _buff (0)
+{
+ reset ();
+}
+
+Resampler::~Resampler (void)
+{
+ clear ();
+}
+
+int
+Resampler::setup (unsigned int fs_inp,
+ unsigned int fs_out,
+ unsigned int nchan,
+ unsigned int hlen)
+{
+ if ((hlen < 8) || (hlen > 96)) return 1;
+ return setup (fs_inp, fs_out, nchan, hlen, 1.0 - 2.6 / hlen);
+}
+
+int
+Resampler::setup (unsigned int fs_inp,
+ unsigned int fs_out,
+ unsigned int nchan,
+ unsigned int hlen,
+ double frel)
+{
+ unsigned int g, h, k, n, s;
+ double r;
+ float *B = 0;
+ Resampler_table *T = 0;
+
+ k = s = 0;
+ if (fs_inp && fs_out && nchan)
+ {
+ r = (double) fs_out / (double) fs_inp;
+ g = gcd (fs_out, fs_inp);
+ n = fs_out / g;
+ s = fs_inp / g;
+ if ((16 * r >= 1) && (n <= 1000))
+ {
+ h = hlen;
+ k = 250;
+ if (r < 1)
+ {
+ frel *= r;
+ h = (unsigned int)(ceil (h / r));
+ k = (unsigned int)(ceil (k / r));
+ }
+ T = Resampler_table::create (frel, h, n);
+ B = new float [nchan * (2 * h - 1 + k)];
+ }
+ }
+ clear ();
+ if (T)
+ {
+ _table = T;
+ _buff = B;
+ _nchan = nchan;
+ _inmax = k;
+ _pstep = s;
+ return reset ();
+ }
+ else return 1;
+}
+
+void
+Resampler::clear (void)
+{
+ Resampler_table::destroy (_table);
+ delete[] _buff;
+ _buff = 0;
+ _table = 0;
+ _nchan = 0;
+ _inmax = 0;
+ _pstep = 0;
+ reset ();
+}
+
+double
+Resampler::inpdist (void) const
+{
+ if (!_table) return 0;
+ return (int)(_table->_hl + 1 - _nread) - (double)_phase / _table->_np;
+}
+
+int
+Resampler::inpsize (void) const
+{
+ if (!_table) return 0;
+ return 2 * _table->_hl;
+}
+
+int
+Resampler::reset (void)
+{
+ if (!_table) return 1;
+
+ inp_count = 0;
+ out_count = 0;
+ inp_data = 0;
+ out_data = 0;
+ _index = 0;
+ _nread = 0;
+ _nzero = 0;
+ _phase = 0;
+ if (_table)
+ {
+ _nread = 2 * _table->_hl;
+ return 0;
+ }
+ return 1;
+}
+
+int
+Resampler::process (void)
+{
+ unsigned int hl, ph, np, dp, in, nr, nz, i, n, c;
+ float *p1, *p2;
+
+ if (!_table) return 1;
+
+ hl = _table->_hl;
+ np = _table->_np;
+ dp = _pstep;
+ in = _index;
+ nr = _nread;
+ ph = _phase;
+ nz = _nzero;
+ n = (2 * hl - nr) * _nchan;
+ p1 = _buff + in * _nchan;
+ p2 = p1 + n;
+
+ while (out_count)
+ {
+ if (nr)
+ {
+ if (inp_count == 0) break;
+ if (inp_data)
+ {
+ for (c = 0; c < _nchan; c++) p2 [c] = inp_data [c];
+ inp_data += _nchan;
+ nz = 0;
+ }
+ else
+ {
+ for (c = 0; c < _nchan; c++) p2 [c] = 0;
+ if (nz < 2 * hl) nz++;
+ }
+ nr--;
+ p2 += _nchan;
+ inp_count--;
+ }
+ else
+ {
+ if (out_data)
+ {
+ if (nz < 2 * hl)
+ {
+ float *c1 = _table->_ctab + hl * ph;
+ float *c2 = _table->_ctab + hl * (np - ph);
+ for (c = 0; c < _nchan; c++)
+ {
+ float *q1 = p1 + c;
+ float *q2 = p2 + c;
+ float s = 1e-20f;
+ for (i = 0; i < hl; i++)
+ {
+ q2 -= _nchan;
+ s += *q1 * c1 [i] + *q2 * c2 [i];
+ q1 += _nchan;
+ }
+ *out_data++ = s - 1e-20f;
+ }
+ }
+ else
+ {
+ for (c = 0; c < _nchan; c++) *out_data++ = 0;
+ }
+ }
+ out_count--;
+
+ ph += dp;
+ if (ph >= np)
+ {
+ nr = ph / np;
+ ph -= nr * np;
+ in += nr;
+ p1 += nr * _nchan;;
+ if (in >= _inmax)
+ {
+ n = (2 * hl - nr) * _nchan;
+ memcpy (_buff, p1, n * sizeof (float));
+ in = 0;
+ p1 = _buff;
+ p2 = p1 + n;
+ }
+ }
+ }
+ }
+ _index = in;
+ _nread = nr;
+ _phase = ph;
+ _nzero = nz;
+
+ return 0;
+}
+
+TruePeakdsp::TruePeakdsp (void)
+ : _m (0)
+ , _p (0)
+ , _res (true)
+ , _buf (NULL)
+{
+}
+
+TruePeakdsp::~TruePeakdsp (void)
+{
+ free(_buf);
+}
+
+void
+TruePeakdsp::process (float const *d, int n)
+{
+ _src.inp_count = n;
+ _src.inp_data = d;
+ _src.out_count = n * 4;
+ _src.out_data = _buf;
+ _src.process ();
+
+ float m = _res ? 0 : _m;
+ float p = _res ? 0 : _p;
+ float v;
+ float *b = _buf;
+ while (n--) {
+ v = fabsf(*b++);
+ if (v > m) m = v;
+ if (v > p) p = v;
+ v = fabsf(*b++);
+ if (v > m) m = v;
+ if (v > p) p = v;
+ v = fabsf(*b++);
+ if (v > m) m = v;
+ if (v > p) p = v;
+ v = fabsf(*b++);
+ if (v > m) m = v;
+ if (v > p) p = v;
+ }
+
+ if (_res) {
+ _m = m;
+ _p = p;
+ _res = false;
+ } else {
+ if (m > _m) { _m = m; }
+ if (p > _p) { _p = p; }
+ }
+}
+
+float
+TruePeakdsp::read (void)
+{
+ _res = true;
+ return _m;
+}
+
+void
+TruePeakdsp::read (float &m, float &p)
+{
+ _res = true;
+ m = _m;
+ p = _p;
+}
+
+void
+TruePeakdsp::reset ()
+{
+ _res = true;
+ _m = 0;
+ _p = 0;
+}
+
+void
+TruePeakdsp::init (float fsamp)
+{
+ _src.setup(fsamp, fsamp * 4.0, 1, 24, 1.0);
+ _buf = (float*) malloc(32768 * sizeof(float));
+
+ /* q/d initialize */
+ float zero[8192];
+ for (int i = 0; i < 8192; ++i) {
+ zero[i]= 0.0;
+ }
+ _src.inp_count = 8192;
+ _src.inp_data = zero;
+ _src.out_count = 32768;
+ _src.out_data = _buf;
+ _src.process ();
+}
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+using std::string;
+using std::vector;
+using std::cerr;
+using std::endl;
+using namespace TruePeakMeter;
+
+VampTruePeak::VampTruePeak(float inputSampleRate)
+ : Plugin(inputSampleRate)
+ , m_blockSize(0)
+{
+}
+
+VampTruePeak::~VampTruePeak()
+{
+}
+
+string
+VampTruePeak::getIdentifier() const
+{
+ return "dBTP";
+}
+
+string
+VampTruePeak::getName() const
+{
+ return "dBTP Meter";
+}
+
+string
+VampTruePeak::getDescription() const
+{
+ return "True Peak Meter (4x Oversampling)";
+}
+
+string
+VampTruePeak::getMaker() const
+{
+ return "Robin Gareus, Fons Adrianesen";
+}
+
+int
+VampTruePeak::getPluginVersion() const
+{
+ return 2;
+}
+
+string
+VampTruePeak::getCopyright() const
+{
+ return "GPL version 3 or later";
+}
+
+bool
+VampTruePeak::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+ if (channels < getMinChannelCount() ||
+ channels > getMaxChannelCount()) {
+ return false;
+ }
+
+ if (blockSize > 8192) {
+ return false;
+ }
+
+ m_blockSize = blockSize;
+
+ _meter.init (m_inputSampleRate);
+
+ return true;
+}
+
+void
+VampTruePeak::reset()
+{
+ _meter.reset ();
+}
+
+VampTruePeak::OutputList
+VampTruePeak::getOutputDescriptors() const
+{
+ OutputList list;
+
+ OutputDescriptor zc;
+ zc.identifier = "level";
+ zc.name = "TruePeak";
+ zc.description = "TruePeak (4x Oversampling)";
+ zc.unit = "dbTP";
+ zc.hasFixedBinCount = true;
+ zc.binCount = 0;
+ zc.hasKnownExtents = false;
+ zc.isQuantized = false;
+ zc.sampleType = OutputDescriptor::OneSamplePerStep;
+ list.push_back(zc);
+
+ return list;
+}
+
+VampTruePeak::FeatureSet
+VampTruePeak::process(const float *const *inputBuffers,
+ Vamp::RealTime timestamp)
+{
+ if (m_blockSize == 0) {
+ cerr << "ERROR: VampTruePeak::process: "
+ << "VampTruePeak has not been initialised"
+ << endl;
+ return FeatureSet();
+ }
+
+ _meter.process (inputBuffers[0], m_blockSize);
+
+ // TODO return momentary
+ return FeatureSet();
+}
+
+VampTruePeak::FeatureSet
+VampTruePeak::getRemainingFeatures()
+{
+ FeatureSet returnFeatures;
+
+ float m, p;
+ _meter.read(m, p);
+
+ Feature dbtp;
+ dbtp.hasTimestamp = false;
+ dbtp.values.push_back(p);
+ returnFeatures[0].push_back(dbtp);
+
+ return returnFeatures;
+}
diff --git a/libs/vamp-plugins/TruePeak.h b/libs/vamp-plugins/TruePeak.h
new file mode 100644
index 0000000000..792788ef6b
--- /dev/null
+++ b/libs/vamp-plugins/TruePeak.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2013-2016 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2006-2012 Fons Adriaensen <fons@linuxaudio.org>
+ * Copyright (C) 2006 Chris Cannam
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _TruePeak_PLUGIN_H_
+#define _TruePeak_PLUGIN_H_
+
+#include <vamp-sdk/Plugin.h>
+
+#include <pthread.h>
+
+namespace TruePeakMeter {
+
+class Resampler_mutex
+{
+private:
+
+ friend class Resampler_table;
+
+ Resampler_mutex (void) { pthread_mutex_init (&_mutex, 0); }
+ ~Resampler_mutex (void) { pthread_mutex_destroy (&_mutex); }
+ void lock (void) { pthread_mutex_lock (&_mutex); }
+ void unlock (void) { pthread_mutex_unlock (&_mutex); }
+
+ pthread_mutex_t _mutex;
+};
+
+class Resampler_table
+{
+private:
+
+ Resampler_table (double fr, unsigned int hl, unsigned int np);
+ ~Resampler_table (void);
+
+ friend class Resampler;
+ friend class VResampler;
+
+ Resampler_table *_next;
+ unsigned int _refc;
+ float *_ctab;
+ double _fr;
+ unsigned int _hl;
+ unsigned int _np;
+
+ static Resampler_table *create (double fr, unsigned int hl, unsigned int np);
+ static void destroy (Resampler_table *T);
+
+ static Resampler_table *_list;
+ static Resampler_mutex _mutex;
+};
+
+class Resampler
+{
+public:
+
+ Resampler (void);
+ ~Resampler (void);
+
+ int setup (unsigned int fs_inp,
+ unsigned int fs_out,
+ unsigned int nchan,
+ unsigned int hlen);
+
+ int setup (unsigned int fs_inp,
+ unsigned int fs_out,
+ unsigned int nchan,
+ unsigned int hlen,
+ double frel);
+
+ void clear (void);
+ int reset (void);
+ int nchan (void) const { return _nchan; }
+ int filtlen (void) const { return inpsize (); } // Deprecated
+ int inpsize (void) const;
+ double inpdist (void) const;
+ int process (void);
+
+ unsigned int inp_count;
+ unsigned int out_count;
+ float const *inp_data;
+ float *out_data;
+ void *inp_list;
+ void *out_list;
+
+private:
+
+ Resampler_table *_table;
+ unsigned int _nchan;
+ unsigned int _inmax;
+ unsigned int _index;
+ unsigned int _nread;
+ unsigned int _nzero;
+ unsigned int _phase;
+ unsigned int _pstep;
+ float *_buff;
+ void *_dummy [8];
+};
+
+class TruePeakdsp
+{
+public:
+
+ TruePeakdsp (void);
+ ~TruePeakdsp (void);
+
+ void process (float const *, int n);
+ float read (void);
+ void read (float &m, float &p);
+ void reset (void);
+
+ void init (float fsamp);
+
+private:
+
+ float _m;
+ float _p;
+ bool _res;
+ float *_buf;
+ Resampler _src;
+};
+
+}; // namespace TruePeakMeter
+
+class VampTruePeak : public Vamp::Plugin
+{
+public:
+ VampTruePeak(float inputSampleRate);
+ virtual ~VampTruePeak();
+
+ size_t getMinChannelCount() const { return 1; }
+ size_t getMaxChannelCount() const { return 1; }
+ size_t getPreferredBlockSize () const { return 1024; }
+ bool initialise(size_t channels, size_t stepSize, size_t blockSize);
+ void reset();
+
+ InputDomain getInputDomain() const { return TimeDomain; }
+
+ std::string getIdentifier() const;
+ std::string getName() const;
+ std::string getDescription() const;
+ std::string getMaker() const;
+ int getPluginVersion() const;
+ std::string getCopyright() const;
+
+ OutputList getOutputDescriptors() const;
+
+ FeatureSet process(const float *const *inputBuffers,
+ Vamp::RealTime timestamp);
+
+ FeatureSet getRemainingFeatures();
+
+protected:
+ size_t m_blockSize;
+
+private:
+ TruePeakMeter::TruePeakdsp _meter;
+};
+
+#endif
diff --git a/libs/vamp-plugins/plugins.cpp b/libs/vamp-plugins/plugins.cpp
index b12a2786c8..732da908fa 100644
--- a/libs/vamp-plugins/plugins.cpp
+++ b/libs/vamp-plugins/plugins.cpp
@@ -47,6 +47,7 @@
#include "AmplitudeFollower.h"
#include "OnsetDetect.h"
#include "EBUr128.h"
+#include "TruePeak.h"
#ifdef HAVE_AUBIO
#include "Onset.h"
#endif
@@ -57,6 +58,7 @@ static Vamp::PluginAdapter<PercussionOnsetDetector> percussionOnsetAdapter;
static Vamp::PluginAdapter<AmplitudeFollower> amplitudeAdapter;
static Vamp::PluginAdapter<OnsetDetector> onsetDetectorAdapter;
static Vamp::PluginAdapter<VampEBUr128> VampEBUr128Adapter;
+static Vamp::PluginAdapter<VampTruePeak> VampTruePeakAdapter;
#ifdef HAVE_AUBIO
static Vamp::PluginAdapter<Onset> onsetAdapter;
#endif
@@ -73,10 +75,10 @@ const VampPluginDescriptor *vampGetPluginDescriptor(unsigned int version,
case 3: return amplitudeAdapter.getDescriptor();
case 4: return onsetDetectorAdapter.getDescriptor();
case 5: return VampEBUr128Adapter.getDescriptor();
+ case 6: return VampTruePeakAdapter.getDescriptor();
#ifdef HAVE_AUBIO
- case 6: return onsetAdapter.getDescriptor();
+ case 7: return onsetAdapter.getDescriptor();
#endif
default: return 0;
}
}
-
diff --git a/libs/vamp-plugins/wscript b/libs/vamp-plugins/wscript
index 131ec03685..856ffe2658 100644
--- a/libs/vamp-plugins/wscript
+++ b/libs/vamp-plugins/wscript
@@ -43,6 +43,7 @@ def build(bld):
OnsetDetect.cpp
PercussionOnsetDetector.cpp
SpectralCentroid.cpp
+ TruePeak.cpp
ZeroCrossing.cpp
'''
obj.export_includes = ['.']