add dBTP Vamp plugin
authorRobin Gareus <robin@gareus.org>
Thu, 11 Feb 2016 09:24:22 +0000 (10:24 +0100)
committerRobin Gareus <robin@gareus.org>
Thu, 11 Feb 2016 13:14:01 +0000 (14:14 +0100)
libs/vamp-plugins/TruePeak.cpp [new file with mode: 0644]
libs/vamp-plugins/TruePeak.h [new file with mode: 0644]
libs/vamp-plugins/plugins.cpp
libs/vamp-plugins/wscript

diff --git a/libs/vamp-plugins/TruePeak.cpp b/libs/vamp-plugins/TruePeak.cpp
new file mode 100644 (file)
index 0000000..3e2ba74
--- /dev/null
@@ -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 (file)
index 0000000..792788e
--- /dev/null
@@ -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
index b12a2786c8c72c809d4515b2ee5fe47dc2f91a82..732da908fa10d60700db0fc30688fa955740238c 100644 (file)
@@ -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;
     }
 }
-
index 131ec03685141948c9ab2d99d164ac1a1f1d52a0..856ffe2658ce730ccbd6cb9c760995fd07802681 100644 (file)
@@ -43,6 +43,7 @@ def build(bld):
             OnsetDetect.cpp
             PercussionOnsetDetector.cpp
             SpectralCentroid.cpp
+            TruePeak.cpp
             ZeroCrossing.cpp
     '''
     obj.export_includes = ['.']