changeset 127:8996465e39fc

Expose some more processing parameters, use a single parameter class
author Chris Cannam <c.cannam@qmul.ac.uk>
date Fri, 16 May 2014 10:12:03 +0100
parents b87290781071
children ca3620c9a763
files Makefile.linux cq/CQInverse.h cq/CQKernel.h cq/CQParameters.h cq/CQSpectrogram.h cq/ConstantQ.h src/CQInverse.cpp src/CQKernel.cpp src/CQSpectrogram.cpp src/ConstantQ.cpp test/processfile.cpp test/test.cpp vamp/CQChromaVamp.cpp vamp/CQVamp.cpp
diffstat 14 files changed, 188 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.linux	Fri May 16 09:06:34 2014 +0100
+++ b/Makefile.linux	Fri May 16 10:12:03 2014 +0100
@@ -4,7 +4,7 @@
 
 CXXFLAGS := $(CFLAGS)
 
-VAMPSDK_DIR := ../vamp-plugin-sdk
+VAMPSDK_DIR := ../../vamp-plugin-sdk
 PLUGIN_LDFLAGS := -shared -Wl,--version-script=vamp/vamp-plugin.map
 
 PLUGIN_EXT := .so
--- a/cq/CQInverse.h	Fri May 16 09:06:34 2014 +0100
+++ b/cq/CQInverse.h	Fri May 16 10:12:03 2014 +0100
@@ -41,9 +41,7 @@
 class CQInverse : public CQBase
 {
 public:
-    CQInverse(double sampleRate,
-	      double minFreq, double maxFreq,
-	      int binsPerOctave);
+    CQInverse(CQParameters params);
     virtual ~CQInverse();
 
     virtual double getSampleRate() const { return m_sampleRate; }
@@ -64,12 +62,13 @@
     RealSequence getRemainingOutput();
 
 private:
-    double m_sampleRate;
-    double m_maxFrequency;
-    double m_minFrequency;
-    int m_binsPerOctave;
+    const CQParameters m_inparams;
+    const double m_sampleRate;
+    const double m_maxFrequency;
+    const double m_minFrequency;
+    const int m_binsPerOctave;
+
     int m_octaves;
-
     CQKernel *m_kernel;
     CQKernel::Properties m_p;
 
--- a/cq/CQKernel.h	Fri May 16 09:06:34 2014 +0100
+++ b/cq/CQKernel.h	Fri May 16 10:12:03 2014 +0100
@@ -32,6 +32,8 @@
 #ifndef CQ_KERNEL_H
 #define CQ_KERNEL_H
 
+#include "CQParameters.h"
+
 #include <vector>
 #include <complex>
 
@@ -40,7 +42,7 @@
 class CQKernel
 {
 public:
-    CQKernel(double sampleRate, double maxFreq, int binsPerOctave);
+    CQKernel(CQParameters params);
     ~CQKernel();
     
     struct Properties {
@@ -66,6 +68,7 @@
         (const std::vector<std::complex<double> > &);
 
 private:
+    const CQParameters m_inparams;
     Properties m_p;
     FFT *m_fft;
 
@@ -75,6 +78,7 @@
     };
     KernelMatrix m_kernel;
 
+    std::vector<double> makeWindow(int len) const;
     void generateKernel();
     void finaliseKernel();
 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cq/CQParameters.h	Fri May 16 10:12:03 2014 +0100
@@ -0,0 +1,75 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+/*
+    Constant-Q library
+    Copyright (c) 2013-2014 Queen Mary, University of London
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#ifndef CQ_PARAMETERS_H
+#define CQ_PARAMETERS_H
+
+class CQParameters
+{
+public:
+    enum WindowType {
+	SqrtBlackmanHarris,
+	SqrtBlackman,
+	SqrtHann,
+	BlackmanHarris,
+	Blackman,
+	Hann,
+    };
+
+    CQParameters(double _sampleRate, 
+		 double _minFrequency, 
+		 double _maxFrequency,
+		 int _binsPerOctave) :
+	sampleRate(_sampleRate),
+	minFrequency(_minFrequency),
+	maxFrequency(_maxFrequency),
+	binsPerOctave(_binsPerOctave),
+	q(1.0),                    // Q scaling factor
+	atomHopFactor(0.25),       // hop size of shortest temporal atom
+	threshold(0.0005),         // sparsity threshold for resulting kernel
+	window(SqrtBlackmanHarris) // window shape
+    { }
+
+    double sampleRate;
+    double minFrequency;
+    double maxFrequency;
+    int binsPerOctave;
+
+    double q;
+    double atomHopFactor;
+    double threshold;
+    WindowType window;
+};
+
+#endif
+
+
+    
--- a/cq/CQSpectrogram.h	Fri May 16 09:06:34 2014 +0100
+++ b/cq/CQSpectrogram.h	Fri May 16 10:12:03 2014 +0100
@@ -43,10 +43,7 @@
 	InterpolateLinear, // linear interpolation between consecutive time cells
     };
 
-    CQSpectrogram(double sampleRate,
-                  double minFreq, double maxFreq,
-                  int binsPerOctave,
-                  Interpolation interpolation);
+    CQSpectrogram(CQParameters params, Interpolation interpolation);
     virtual ~CQSpectrogram();
 
     virtual double getSampleRate() const { return m_cq.getSampleRate(); }
--- a/cq/ConstantQ.h	Fri May 16 09:06:34 2014 +0100
+++ b/cq/ConstantQ.h	Fri May 16 10:12:03 2014 +0100
@@ -33,6 +33,7 @@
 #define CONSTANTQ_H
 
 #include "CQBase.h"
+#include "CQParameters.h"
 #include "CQKernel.h"
 
 class Resampler;
@@ -48,9 +49,7 @@
 class ConstantQ : public CQBase
 {
 public:
-    ConstantQ(double sampleRate, 
-	      double minFreq, double maxFreq, 
-	      int binsPerOctave);
+    ConstantQ(CQParameters params);
     virtual ~ConstantQ();
 
     virtual double getSampleRate() const { return m_sampleRate; }
@@ -92,12 +91,13 @@
     ComplexBlock getRemainingOutput();
 
 private:
-    double m_sampleRate;
-    double m_maxFrequency;
-    double m_minFrequency;
-    int m_binsPerOctave;
+    const CQParameters m_inparams;
+    const double m_sampleRate;
+    const double m_maxFrequency;
+    const double m_minFrequency;
+    const int m_binsPerOctave;
+
     int m_octaves;
-
     CQKernel *m_kernel;
     CQKernel::Properties m_p;
     int m_bigBlockSize;
--- a/src/CQInverse.cpp	Fri May 16 09:06:34 2014 +0100
+++ b/src/CQInverse.cpp	Fri May 16 10:12:03 2014 +0100
@@ -45,17 +45,15 @@
 
 //#define DEBUG_CQ 1
 
-CQInverse::CQInverse(double sampleRate,
-                     double minFreq,
-                     double maxFreq,
-                     int binsPerOctave) :
-    m_sampleRate(sampleRate),
-    m_maxFrequency(maxFreq),
-    m_minFrequency(minFreq),
-    m_binsPerOctave(binsPerOctave),
+CQInverse::CQInverse(CQParameters params) :
+    m_inparams(params),
+    m_sampleRate(params.sampleRate),
+    m_maxFrequency(params.maxFrequency),
+    m_minFrequency(params.minFrequency),
+    m_binsPerOctave(params.binsPerOctave),
     m_fft(0)
 {
-    if (minFreq <= 0.0 || maxFreq <= 0.0) {
+    if (m_minFrequency <= 0.0 || m_maxFrequency <= 0.0) {
         throw std::invalid_argument("Frequency extents must be positive");
     }
 
@@ -87,7 +85,7 @@
 CQInverse::initialise()
 {
     m_octaves = int(ceil(log2(m_maxFrequency / m_minFrequency)));
-    m_kernel = new CQKernel(m_sampleRate, m_maxFrequency, m_binsPerOctave);
+    m_kernel = new CQKernel(m_inparams);
     m_p = m_kernel->getProperties();
     
     // Use exact powers of two for resampling rates. They don't have
--- a/src/CQKernel.cpp	Fri May 16 09:06:34 2014 +0100
+++ b/src/CQKernel.cpp	Fri May 16 10:12:03 2014 +0100
@@ -48,12 +48,13 @@
 
 typedef std::complex<double> C;
 
-CQKernel::CQKernel(double sampleRate, double maxFreq, int binsPerOctave) :
+CQKernel::CQKernel(CQParameters params) :
+    m_inparams(params),
     m_fft(0)
 {
-    m_p.sampleRate = sampleRate;
-    m_p.maxFrequency = maxFreq;
-    m_p.binsPerOctave = binsPerOctave;
+    m_p.sampleRate = params.sampleRate;
+    m_p.maxFrequency = params.maxFrequency;
+    m_p.binsPerOctave = params.binsPerOctave;
     generateKernel();
 }
 
@@ -62,12 +63,60 @@
     delete m_fft;
 }
 
+vector<double>
+CQKernel::makeWindow(int len) const
+{
+    // The MATLAB version uses a symmetric window, but our windows
+    // are periodic. A symmetric window of size N is a periodic
+    // one of size N-1 with the first element stuck on the end.
+
+    WindowType wt(BlackmanHarrisWindow);
+
+    switch (m_inparams.window) {
+    case CQParameters::SqrtBlackmanHarris:
+    case CQParameters::BlackmanHarris:
+        wt = BlackmanHarrisWindow;
+        break;
+    case CQParameters::SqrtBlackman:
+    case CQParameters::Blackman:
+        wt = BlackmanWindow;
+        break;
+    case CQParameters::SqrtHann:
+    case CQParameters::Hann:
+        wt = HanningWindow;
+        break;
+    }
+
+    Window<double> w(wt, len-1);
+    vector<double> win = w.getWindowData();
+    win.push_back(win[0]);
+
+    switch (m_inparams.window) {
+    case CQParameters::SqrtBlackmanHarris:
+    case CQParameters::SqrtBlackman:
+    case CQParameters::SqrtHann:
+        for (int i = 0; i < (int)win.size(); ++i) {
+            win[i] = sqrt(win[i]) / len;
+        }
+        break;
+    case CQParameters::BlackmanHarris:
+    case CQParameters::Blackman:
+    case CQParameters::Hann:
+        for (int i = 0; i < (int)win.size(); ++i) {
+            win[i] = win[i] / len;
+        }
+        break;
+    }
+
+    return win;
+}
+
 void
 CQKernel::generateKernel()
 {
-    double q = 1;
-    double atomHopFactor = 0.25;
-    double thresh = 0.0005;
+    double q = m_inparams.q;
+    double atomHopFactor = m_inparams.atomHopFactor;
+    double thresh = m_inparams.threshold;
 
     double bpo = m_p.binsPerOctave;
 
@@ -99,7 +148,7 @@
     m_p.atomsPerFrame = floor
         (1.0 + (m_p.fftSize - ceil(maxNK / 2.0) - m_p.firstCentre) / m_p.atomSpacing);
 
-    cerr << "atomsPerFrame = " << m_p.atomsPerFrame << " (atomHopFactor = " << atomHopFactor << ", atomSpacing = " << m_p.atomSpacing << ", fftSize = " << m_p.fftSize << ", maxNK = " << maxNK << ", firstCentre = " << m_p.firstCentre << ")" << endl;
+    cerr << "atomsPerFrame = " << m_p.atomsPerFrame << " (q = " << q << ", Q = " << m_p.Q << ", atomHopFactor = " << atomHopFactor << ", atomSpacing = " << m_p.atomSpacing << ", fftSize = " << m_p.fftSize << ", maxNK = " << maxNK << ", firstCentre = " << m_p.firstCentre << ")" << endl;
 
     m_p.lastCentre = m_p.firstCentre + (m_p.atomsPerFrame - 1) * m_p.atomSpacing;
 
@@ -114,16 +163,7 @@
         int nk = round(m_p.Q * m_p.sampleRate /
                        (m_p.minFrequency * pow(2, ((k-1.0) / bpo))));
 
-        // The MATLAB version uses a symmetric window, but our windows
-        // are periodic. A symmetric window of size N is a periodic
-        // one of size N-1 with the first element stuck on the end
-        Window<double> w(BlackmanHarrisWindow, nk-1);
-        vector<double> win = w.getWindowData();
-        win.push_back(win[0]);
-
-        for (int i = 0; i < (int)win.size(); ++i) {
-            win[i] = sqrt(win[i]) / nk;
-        }
+        vector<double> win = makeWindow(nk);
 
         double fk = m_p.minFrequency * pow(2, ((k-1.0) / bpo));
 
@@ -242,7 +282,7 @@
     }
 
     vector<double> wK;
-    double q = 1; //!!! duplicated from constructor
+    double q = m_inparams.q;
     for (int i = round(1.0/q); i < ncols - round(1.0/q) - 2; ++i) {
         wK.push_back(abs(square[i][i]));
     }
--- a/src/CQSpectrogram.cpp	Fri May 16 09:06:34 2014 +0100
+++ b/src/CQSpectrogram.cpp	Fri May 16 10:12:03 2014 +0100
@@ -37,11 +37,9 @@
 using std::cerr;
 using std::endl;
 
-CQSpectrogram::CQSpectrogram(double sampleRate,
-			       double minFreq, double maxFreq,
-			       int binsPerOctave,
-			       Interpolation interpolation) :
-    m_cq(sampleRate, minFreq, maxFreq, binsPerOctave),
+CQSpectrogram::CQSpectrogram(CQParameters params,
+                             Interpolation interpolation) :
+    m_cq(params),
     m_interpolation(interpolation)
 {
 }
--- a/src/ConstantQ.cpp	Fri May 16 09:06:34 2014 +0100
+++ b/src/ConstantQ.cpp	Fri May 16 10:12:03 2014 +0100
@@ -47,17 +47,15 @@
 
 //#define DEBUG_CQ 1
 
-ConstantQ::ConstantQ(double sampleRate,
-                     double minFreq,
-                     double maxFreq,
-                     int binsPerOctave) :
-    m_sampleRate(sampleRate),
-    m_maxFrequency(maxFreq),
-    m_minFrequency(minFreq),
-    m_binsPerOctave(binsPerOctave),
+ConstantQ::ConstantQ(CQParameters params) :
+    m_inparams(params),
+    m_sampleRate(params.sampleRate),
+    m_maxFrequency(params.maxFrequency),
+    m_minFrequency(params.minFrequency),
+    m_binsPerOctave(params.binsPerOctave),
     m_fft(0)
 {
-    if (minFreq <= 0.0 || maxFreq <= 0.0) {
+    if (m_minFrequency <= 0.0 || m_maxFrequency <= 0.0) {
         throw std::invalid_argument("Frequency extents must be positive");
     }
 
@@ -89,7 +87,7 @@
 ConstantQ::initialise()
 {
     m_octaves = int(ceil(log2(m_maxFrequency / m_minFrequency)));
-    m_kernel = new CQKernel(m_sampleRate, m_maxFrequency, m_binsPerOctave);
+    m_kernel = new CQKernel(m_inparams);
     m_p = m_kernel->getProperties();
     
     // Use exact powers of two for resampling rates. They don't have
--- a/test/processfile.cpp	Fri May 16 09:06:34 2014 +0100
+++ b/test/processfile.cpp	Fri May 16 10:12:03 2014 +0100
@@ -125,8 +125,9 @@
     if (minFreq == 0.0) minFreq = 100;
     if (bpo == 0) bpo = 60;
 
-    ConstantQ cq(sfinfo.samplerate, minFreq, maxFreq, bpo);
-    CQInverse cqi(sfinfo.samplerate, minFreq, maxFreq, bpo);
+    CQParameters params(sfinfo.samplerate, minFreq, maxFreq, bpo);
+    ConstantQ cq(params);
+    CQInverse cqi(params);
 
     cerr << "max freq = " << cq.getMaxFrequency() << ", min freq = "
 	 << cq.getMinFrequency() << ", octaves = " << cq.getOctaves() << endl;
--- a/test/test.cpp	Fri May 16 09:06:34 2014 +0100
+++ b/test/test.cpp	Fri May 16 10:12:03 2014 +0100
@@ -50,7 +50,8 @@
 	in.push_back(sin(i * M_PI / 2.0));
     }
 
-    CQSpectrogram k(8, 1, 4, 4, CQSpectrogram::InterpolateZeros);
+    CQParameters params(8, 1, 4, 4);
+    CQSpectrogram k(params, CQSpectrogram::InterpolateZeros);
 
     vector<vector<double> > out = k.process(in);
     vector<vector<double> > rest = k.getRemainingOutput();
--- a/vamp/CQChromaVamp.cpp	Fri May 16 09:06:34 2014 +0100
+++ b/vamp/CQChromaVamp.cpp	Fri May 16 10:12:03 2014 +0100
@@ -228,9 +228,8 @@
          << ", min freq " << m_minFrequency << ", max freq " << m_maxFrequency
          << endl;
 
-    m_cq = new CQSpectrogram
-	(m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo,
-         CQSpectrogram::InterpolateLinear);
+    CQParameters p(m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo);
+    m_cq = new CQSpectrogram(p, CQSpectrogram::InterpolateLinear);
 
     return true;
 }
@@ -240,9 +239,8 @@
 {
     if (m_cq) {
 	delete m_cq;
-	m_cq = new CQSpectrogram
-	    (m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo,
-             CQSpectrogram::InterpolateLinear);
+        CQParameters p(m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo);
+        m_cq = new CQSpectrogram(p, CQSpectrogram::InterpolateLinear);
     }
     m_haveStartTime = false;
     m_columnCount = 0;
--- a/vamp/CQVamp.cpp	Fri May 16 09:06:34 2014 +0100
+++ b/vamp/CQVamp.cpp	Fri May 16 10:12:03 2014 +0100
@@ -286,9 +286,8 @@
             (m_maxMIDIPitch, 0, m_tuningFrequency);
     }
 
-    m_cq = new CQSpectrogram
-	(m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo,
-         m_interpolation);
+    CQParameters p(m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo);
+    m_cq = new CQSpectrogram(p, m_interpolation);
 
     return true;
 }
@@ -298,9 +297,8 @@
 {
     if (m_cq) {
 	delete m_cq;
-	m_cq = new CQSpectrogram
-	    (m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo,
-             m_interpolation);
+        CQParameters p(m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo);
+        m_cq = new CQSpectrogram(p, m_interpolation);
     }
     m_haveStartTime = false;
     m_columnCount = 0;