changeset 195:4e57fb34d9e6

Merge
author Chris Cannam
date Wed, 07 Oct 2015 11:15:13 +0100
parents 26daede606a8 (diff) 786d446fe22b (current diff)
children be1fc3a6b901
files
diffstat 17 files changed, 815 insertions(+), 168 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Wed Oct 07 09:33:28 2015 +0100
+++ b/.hgignore	Wed Oct 07 11:15:13 2015 +0100
@@ -5,5 +5,7 @@
 *.a
 doc/html/
 tests/test-*
+tests/test.log
 *.rej
+ext/uncertain/
 
--- a/build/general/Makefile.inc	Wed Oct 07 09:33:28 2015 +0100
+++ b/build/general/Makefile.inc	Wed Oct 07 11:15:13 2015 +0100
@@ -38,6 +38,7 @@
            dsp/tonal/ChangeDetectionFunction.h \
            dsp/tonal/TCSgram.h \
            dsp/tonal/TonalEstimator.h \
+           dsp/transforms/DCT.h \
            dsp/transforms/FFT.h \
            dsp/wavelet/Wavelet.h \
            hmm/hmm.h \
@@ -83,6 +84,7 @@
            dsp/tonal/ChangeDetectionFunction.cpp \
            dsp/tonal/TCSgram.cpp \
            dsp/tonal/TonalEstimator.cpp \
+           dsp/transforms/DCT.cpp \
            dsp/transforms/FFT.cpp \
            dsp/wavelet/Wavelet.cpp \
            hmm/hmm.c \
--- a/dsp/signalconditioning/DFProcess.cpp	Wed Oct 07 09:33:28 2015 +0100
+++ b/dsp/signalconditioning/DFProcess.cpp	Wed Oct 07 11:15:13 2015 +0100
@@ -59,13 +59,11 @@
     filtSrc = new double[ m_length ];
     filtDst = new double[ m_length ];
 
-	
-    //Low Pass Smoothing Filter Config
-    m_FilterConfigParams.ord = Config.LPOrd;
-    m_FilterConfigParams.ACoeffs = Config.LPACoeffs;
-    m_FilterConfigParams.BCoeffs = Config.LPBCoeffs;
-	
-    m_FiltFilt = new FiltFilt( m_FilterConfigParams );
+    Filter::Parameters params;
+    params.a = std::vector<double>(Config.LPACoeffs, Config.LPACoeffs + Config.LPOrd + 1);
+    params.b = std::vector<double>(Config.LPBCoeffs, Config.LPBCoeffs + Config.LPOrd + 1);
+    
+    m_FiltFilt = new FiltFilt(params);
 	
     //add delta threshold
     m_delta = Config.delta;
--- a/dsp/signalconditioning/DFProcess.h	Wed Oct 07 09:33:28 2015 +0100
+++ b/dsp/signalconditioning/DFProcess.h	Wed Oct 07 11:15:13 2015 +0100
@@ -81,8 +81,6 @@
     double* m_filtScratchIn;
     double* m_filtScratchOut;
 
-    FilterConfig m_FilterConfigParams;
-
     FiltFilt* m_FiltFilt;
 
     bool m_isMedianPositive;
--- a/dsp/signalconditioning/FiltFilt.cpp	Wed Oct 07 09:33:28 2015 +0100
+++ b/dsp/signalconditioning/FiltFilt.cpp	Wed Oct 07 11:15:13 2015 +0100
@@ -19,36 +19,16 @@
 // Construction/Destruction
 //////////////////////////////////////////////////////////////////////
 
-FiltFilt::FiltFilt( FilterConfig Config )
+FiltFilt::FiltFilt(Filter::Parameters parameters) :
+    m_filter(parameters)
 {
-    m_filtScratchIn = NULL;
-    m_filtScratchOut = NULL;
-    m_ord = 0;
-	
-    initialise( Config );
+    m_ord = m_filter.getOrder();
 }
 
 FiltFilt::~FiltFilt()
 {
-    deInitialise();
 }
 
-void FiltFilt::initialise( FilterConfig Config )
-{
-    m_ord = Config.ord;
-    m_filterConfig.ord = Config.ord;
-    m_filterConfig.ACoeffs = Config.ACoeffs;
-    m_filterConfig.BCoeffs = Config.BCoeffs;
-	
-    m_filter = new Filter( m_filterConfig );
-}
-
-void FiltFilt::deInitialise()
-{
-    delete m_filter;
-}
-
-
 void FiltFilt::process(double *src, double *dst, unsigned int length)
 {	
     unsigned int i;
@@ -59,14 +39,13 @@
     unsigned int nFact = 3 * ( nFilt - 1);
     unsigned int nExt	= length + 2 * nFact;
 
-    m_filtScratchIn = new double[ nExt ];
-    m_filtScratchOut = new double[ nExt ];
-
+    double *filtScratchIn = new double[ nExt ];
+    double *filtScratchOut = new double[ nExt ];
 	
     for( i = 0; i< nExt; i++ ) 
     {
-	m_filtScratchIn[ i ] = 0.0;
-	m_filtScratchOut[ i ] = 0.0;
+	filtScratchIn[ i ] = 0.0;
+	filtScratchOut[ i ] = 0.0;
     }
 
     // Edge transients reflection
@@ -76,51 +55,51 @@
     unsigned int index = 0;
     for( i = nFact; i > 0; i-- )
     {
-	m_filtScratchIn[ index++ ] = sample0 - src[ i ];
+	filtScratchIn[ index++ ] = sample0 - src[ i ];
     }
     index = 0;
     for( i = 0; i < nFact; i++ )
     {
-	m_filtScratchIn[ (nExt - nFact) + index++ ] = sampleN - src[ (length - 2) - i ];
+	filtScratchIn[ (nExt - nFact) + index++ ] = sampleN - src[ (length - 2) - i ];
     }
 
     index = 0;
     for( i = 0; i < length; i++ )
     {
-	m_filtScratchIn[ i + nFact ] = src[ i ];
+	filtScratchIn[ i + nFact ] = src[ i ];
     }
 	
     ////////////////////////////////
     // Do  0Ph filtering
-    m_filter->process( m_filtScratchIn, m_filtScratchOut, nExt);
+    m_filter.process( filtScratchIn, filtScratchOut, nExt);
 	
     // reverse the series for FILTFILT 
     for ( i = 0; i < nExt; i++)
     { 
-	m_filtScratchIn[ i ] = m_filtScratchOut[ nExt - i - 1];
+	filtScratchIn[ i ] = filtScratchOut[ nExt - i - 1];
     }
 
     // do FILTER again 
-    m_filter->process( m_filtScratchIn, m_filtScratchOut, nExt);
+    m_filter.process( filtScratchIn, filtScratchOut, nExt);
 	
     // reverse the series back 
     for ( i = 0; i < nExt; i++)
     {
-	m_filtScratchIn[ i ] = m_filtScratchOut[ nExt - i - 1 ];
+	filtScratchIn[ i ] = filtScratchOut[ nExt - i - 1 ];
     }
     for ( i = 0;i < nExt; i++)
     {
-	m_filtScratchOut[ i ] = m_filtScratchIn[ i ];
+	filtScratchOut[ i ] = filtScratchIn[ i ];
     }
 
     index = 0;
     for( i = 0; i < length; i++ )
     {
-	dst[ index++ ] = m_filtScratchOut[ i + nFact ];
+	dst[ index++ ] = filtScratchOut[ i + nFact ];
     }	
 
-    delete [] m_filtScratchIn;
-    delete [] m_filtScratchOut;
+    delete [] filtScratchIn;
+    delete [] filtScratchOut;
 
 }
 
--- a/dsp/signalconditioning/FiltFilt.h	Wed Oct 07 09:33:28 2015 +0100
+++ b/dsp/signalconditioning/FiltFilt.h	Wed Oct 07 11:15:13 2015 +0100
@@ -20,30 +20,21 @@
 
 /**
  * Zero-phase digital filter, implemented by processing the data
- * through a filter specified by the given FilterConfig structure (see
+ * through a filter specified by the given filter parameters (see
  * Filter) and then processing it again in reverse.
  */
 class FiltFilt  
 {
 public:
-    FiltFilt( FilterConfig Config );
+    FiltFilt(Filter::Parameters);
     virtual ~FiltFilt();
 
     void reset();
     void process( double* src, double* dst, unsigned int length );
 
 private:
-    void initialise( FilterConfig Config );
-    void deInitialise();
-
-    unsigned int m_ord;
-
-    Filter* m_filter;
-
-    double* m_filtScratchIn;
-    double* m_filtScratchOut;
-
-    FilterConfig m_filterConfig;
+    Filter m_filter;
+    int m_ord;
 };
 
 #endif
--- a/dsp/signalconditioning/Filter.cpp	Wed Oct 07 09:33:28 2015 +0100
+++ b/dsp/signalconditioning/Filter.cpp	Wed Oct 07 11:15:13 2015 +0100
@@ -4,7 +4,6 @@
     QM DSP Library
 
     Centre for Digital Music, Queen Mary, University of London.
-    This file 2005-2006 Christian Landone.
 
     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License as
@@ -15,73 +14,112 @@
 
 #include "Filter.h"
 
-//////////////////////////////////////////////////////////////////////
-// Construction/Destruction
-//////////////////////////////////////////////////////////////////////
+#include <stdexcept>
 
-Filter::Filter( FilterConfig Config )
+using namespace std;
+
+Filter::Filter(Parameters params)
 {
-    m_ord = 0;
-    m_outBuffer = NULL;
-    m_inBuffer = NULL;
+    if (params.a.empty()) {
+        m_fir = true;
+        if (params.b.empty()) {
+            throw logic_error("Filter must have at least one pair of coefficients");
+        }
+    } else {
+        m_fir = false;
+        if (params.a.size() != params.b.size()) {
+            throw logic_error("Inconsistent numbers of filter coefficients");
+        }
+    }
+    
+    m_sz = int(params.b.size());
+    m_order = m_sz - 1;
 
-    initialise( Config );
+    m_a = params.a;
+    m_b = params.b;
+    
+    // We keep some empty space at the start of the buffer, and
+    // encroach gradually into it as we add individual sample
+    // calculations at the start. Then when we run out of space, we
+    // move the buffer back to the end and begin again. This is
+    // significantly faster than moving the whole buffer along in
+    // 1-sample steps every time.
+
+    m_offmax = 20;
+    m_offa = m_offmax;
+    m_offb = m_offmax;
+
+    if (!m_fir) {
+        m_bufa.resize(m_order + m_offmax);
+    }
+
+    m_bufb.resize(m_sz + m_offmax);
 }
 
 Filter::~Filter()
 {
-    deInitialise();
 }
 
-void Filter::initialise( FilterConfig Config )
+void
+Filter::reset()
 {
-    m_ord = Config.ord;
-    m_ACoeffs = Config.ACoeffs;
-    m_BCoeffs = Config.BCoeffs;
+    m_offb = m_offmax;
+    m_offa = m_offmax;
 
-    m_inBuffer = new double[ m_ord + 1 ];
-    m_outBuffer = new double[ m_ord + 1 ];
+    if (!m_fir) {
+        m_bufa.assign(m_bufa.size(), 0.0);
+    }
 
-    reset();
+    m_bufb.assign(m_bufb.size(), 0.0);
 }
 
-void Filter::deInitialise()
+void
+Filter::process(const double *const __restrict__ in,
+		double *const __restrict__ out,
+		const int n)
 {
-    delete[] m_inBuffer;
-    delete[] m_outBuffer;
+    for (int s = 0; s < n; ++s) {
+
+        if (m_offb > 0) --m_offb;
+        else {
+            for (int i = m_sz - 2; i >= 0; --i) {
+                m_bufb[i + m_offmax + 1] = m_bufb[i];
+            }
+            m_offb = m_offmax;
+        }
+        m_bufb[m_offb] = in[s];
+
+        double b_sum = 0.0;
+        for (int i = 0; i < m_sz; ++i) {
+            b_sum += m_b[i] * m_bufb[i + m_offb];
+        }
+
+        double outval;
+
+        if (m_fir) {
+
+            outval = b_sum;
+
+        } else {
+
+            double a_sum = 0.0;
+            for (int i = 0; i < m_order; ++i) {
+                a_sum += m_a[i + 1] * m_bufa[i + m_offa];
+            }
+
+            outval = b_sum - a_sum;
+
+            if (m_offa > 0) --m_offa;
+            else {
+                for (int i = m_order - 2; i >= 0; --i) {
+                    m_bufa[i + m_offmax + 1] = m_bufa[i];
+                }
+                m_offa = m_offmax;
+            }
+            m_bufa[m_offa] = outval;
+        }
+        
+	out[s] = outval;
+    }
 }
 
-void Filter::reset()
-{
-    for( unsigned int i = 0; i < m_ord+1; i++ ){ m_inBuffer[ i ] = 0.0; }
-    for(unsigned int  i = 0; i < m_ord+1; i++ ){ m_outBuffer[ i ] = 0.0; }
-}
-
-void Filter::process( double *src, double *dst, unsigned int length )
-{
-    unsigned int SP,i,j;
-
-    double xin,xout;
-
-    for (SP=0;SP<length;SP++)
-    {
-        xin=src[SP];
-        /* move buffer */
-        for ( i = 0; i < m_ord; i++) {m_inBuffer[ m_ord - i ]=m_inBuffer[ m_ord - i - 1 ];}
-        m_inBuffer[0]=xin;
-
-        xout=0.0;
-        for (j=0;j< m_ord + 1; j++)
-	    xout = xout + m_BCoeffs[ j ] * m_inBuffer[ j ];
-        for (j = 0; j < m_ord; j++)
-	    xout= xout - m_ACoeffs[ j + 1 ] * m_outBuffer[ j ];
-
-        dst[ SP ] = xout;
-        for ( i = 0; i < m_ord - 1; i++ ) { m_outBuffer[ m_ord - i - 1 ] = m_outBuffer[ m_ord - i - 2 ];}
-        m_outBuffer[0]=xout;
-
-    } /* end of SP loop */
-}
-
-
-
--- a/dsp/signalconditioning/Filter.h	Wed Oct 07 09:33:28 2015 +0100
+++ b/dsp/signalconditioning/Filter.h	Wed Oct 07 11:15:13 2015 +0100
@@ -4,7 +4,6 @@
     QM DSP Library
 
     Centre for Digital Music, Queen Mary, University of London.
-    This file 2005-2006 Christian Landone.
 
     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License as
@@ -16,46 +15,53 @@
 #ifndef FILTER_H
 #define FILTER_H
 
-#ifndef NULL
-#define NULL 0
-#endif
+#include <vector>
 
-/**
- * Filter specification. For a filter of order ord, the ACoeffs and
- * BCoeffs arrays must point to ord+1 values each. ACoeffs provides
- * the denominator and BCoeffs the numerator coefficients of the
- * filter.
- */
-struct FilterConfig{
-    unsigned int ord;
-    double* ACoeffs;
-    double* BCoeffs;
-};
-
-/**
- * Digital filter specified through FilterConfig structure.
- */
-class Filter  
+class Filter
 {
 public:
-    Filter( FilterConfig Config );
-    virtual ~Filter();
+    struct Parameters {
+        std::vector<double> a;
+        std::vector<double> b;
+    };
+
+    /**
+     * Construct an IIR filter with numerators b and denominators
+     * a. The filter will have order b.size()-1. To make an FIR
+     * filter, leave the vector a in the param struct empty.
+     * Otherwise, a and b must have the same number of values.
+     */
+    Filter(Parameters params);
+    
+    ~Filter();
 
     void reset();
 
-    void process( double *src, double *dst, unsigned int length );
+    /**
+     * Filter the input sequence \arg in of length \arg n samples, and
+     * write the resulting \arg n samples into \arg out. There must be
+     * enough room in \arg out for \arg n samples to be written.
+     */
+    void process(const double *const __restrict__ in,
+                 double *const __restrict__ out,
+                 const int n);
 
+    int getOrder() const { return m_order; }
+    
 private:
-    void initialise( FilterConfig Config );
-    void deInitialise();
+    int m_order;
+    int m_sz;
+    std::vector<double> m_a;
+    std::vector<double> m_b;
+    std::vector<double> m_bufa;
+    std::vector<double> m_bufb;
+    int m_offa;
+    int m_offb;
+    int m_offmax;
+    bool m_fir;
 
-    unsigned int m_ord;
-
-    double* m_inBuffer;
-    double* m_outBuffer;
-
-    double* m_ACoeffs;
-    double* m_BCoeffs;
+    Filter(const Filter &); // not supplied
+    Filter &operator=(const Filter &); // not supplied
 };
-
+    
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dsp/transforms/DCT.cpp	Wed Oct 07 11:15:13 2015 +0100
@@ -0,0 +1,91 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    QM DSP Library
+
+    Centre for Digital Music, Queen Mary, University of London.
+    This file by 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 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "DCT.h"
+
+#include <cmath>
+
+DCT::DCT(int n) :
+    m_n(n),
+    m_scaled(n, 0.0),
+    m_time2(n*4, 0.0),
+    m_freq2r(n*4, 0.0),
+    m_freq2i(n*4, 0.0),
+    m_fft(n*4)
+{
+    m_scale = m_n * sqrt(2.0 / m_n);
+}
+
+DCT::~DCT()
+{
+}
+
+void
+DCT::forward(const double *in, double *out)
+{
+    for (int i = 0; i < m_n; ++i) {
+	m_time2[i*2 + 1] = in[i];
+	m_time2[m_n*4 - i*2 - 1] = in[i];
+    }
+
+    m_fft.forward(m_time2.data(), m_freq2r.data(), m_freq2i.data());
+
+    for (int i = 0; i < m_n; ++i) {
+	out[i] = m_freq2r[i];
+    }
+}
+
+void
+DCT::forwardUnitary(const double *in, double *out)
+{
+    forward(in, out);
+    for (int i = 0; i < m_n; ++i) {
+	out[i] /= m_scale;
+    }
+    out[0] /= sqrt(2.0);
+}
+
+void
+DCT::inverse(const double *in, double *out)
+{
+    for (int i = 0; i < m_n; ++i) {
+	m_freq2r[i] = in[i];
+    }
+    for (int i = 0; i < m_n; ++i) {
+	m_freq2r[m_n*2 - i] = -in[i];
+    }
+    m_freq2r[m_n] = 0.0;
+
+    for (int i = 0; i <= m_n*2; ++i) {
+	m_freq2i[i] = 0.0;
+    }
+    
+    m_fft.inverse(m_freq2r.data(), m_freq2i.data(), m_time2.data());
+
+    for (int i = 0; i < m_n; ++i) {
+	out[i] = m_time2[i*2 + 1];
+    }
+}
+
+void
+DCT::inverseUnitary(const double *in, double *out)
+{
+    for (int i = 0; i < m_n; ++i) {
+	m_scaled[i] = in[i] * m_scale;
+    }
+    m_scaled[0] *= sqrt(2.0);
+    inverse(m_scaled.data(), out);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dsp/transforms/DCT.h	Wed Oct 07 11:15:13 2015 +0100
@@ -0,0 +1,85 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    QM DSP Library
+
+    Centre for Digital Music, Queen Mary, University of London.
+    This file by 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 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef DCT_H
+#define DCT_H
+
+#include "FFT.h"
+
+#include <vector>
+
+class DCT
+{
+public:
+    /**
+     * Construct a DCT object to calculate the Discrete Cosine
+     * Transform given input of size n samples. The transform is
+     * implemented using an FFT of size 4n, for simplicity.
+     */
+    DCT(int n);
+
+    ~DCT();
+
+    /**
+     * Carry out a type-II DCT of size n, where n is the value
+     * provided to the constructor above.
+     *
+     * The in and out pointers must point to (enough space for) n
+     * values.
+     */
+    void forward(const double *in, double *out);
+
+    /**
+     * Carry out a type-II unitary DCT of size n, where n is the value
+     * provided to the constructor above. This is a scaled version of
+     * the type-II DCT corresponding to a transform with an orthogonal
+     * matrix. This is the transform implemented by the dct() function
+     * in MATLAB.
+     *
+     * The in and out pointers must point to (enough space for) n
+     * values.
+     */
+    void forwardUnitary(const double *in, double *out);
+
+    /**
+     * Carry out a type-III (inverse) DCT of size n, where n is the
+     * value provided to the constructor above.
+     *
+     * The in and out pointers must point to (enough space for) n
+     * values.
+     */
+    void inverse(const double *in, double *out);
+
+    /**
+     * Carry out a type-III (inverse) unitary DCT of size n, where n
+     * is the value provided to the constructor above. This is the
+     * inverse of forwardUnitary().
+     *
+     * The in and out pointers must point to (enough space for) n
+     * values.
+     */
+    void inverseUnitary(const double *in, double *out);
+
+private:
+    int m_n;
+    double m_scale;
+    std::vector<double> m_scaled;
+    std::vector<double> m_time2;
+    std::vector<double> m_freq2r;
+    std::vector<double> m_freq2i;
+    FFTReal m_fft;
+};
+
+#endif
--- a/dsp/transforms/FFT.h	Wed Oct 07 09:33:28 2015 +0100
+++ b/dsp/transforms/FFT.h	Wed Oct 07 11:15:13 2015 +0100
@@ -4,6 +4,12 @@
     QM DSP Library
 
     Centre for Digital Music, Queen Mary, University of London.
+
+    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.  See the file
+    COPYING included with this distribution for more information.
 */
 
 #ifndef FFT_H
--- a/maths/MathUtilities.cpp	Wed Oct 07 09:33:28 2015 +0100
+++ b/maths/MathUtilities.cpp	Wed Oct 07 11:15:13 2015 +0100
@@ -20,6 +20,7 @@
 #include <vector>
 #include <cmath>
 
+using namespace std;
 
 double MathUtilities::mod(double x, double y)
 {
@@ -56,7 +57,7 @@
     *ANorm = a;
 }
 
-double MathUtilities::getAlphaNorm( const std::vector <double> &data, int alpha )
+double MathUtilities::getAlphaNorm( const vector <double> &data, int alpha )
 {
     int i;
     int len = data.size();
@@ -66,7 +67,6 @@
     for( i = 0; i < len; i++)
     {
 	temp = data[ i ];
-		
 	a  += ::pow( fabs(temp), double(alpha) );
     }
     a /= ( double )len;
@@ -88,9 +88,9 @@
 {
     if (len == 0) return 0;
     
-    std::vector<double> scratch;
+    vector<double> scratch;
     for (int i = 0; i < len; ++i) scratch.push_back(src[i]);
-    std::sort(scratch.begin(), scratch.end());
+    sort(scratch.begin(), scratch.end());
 
     int middle = len/2;
     if (len % 2 == 0) {
@@ -126,7 +126,7 @@
     return retVal;
 }
 
-double MathUtilities::mean(const std::vector<double> &src,
+double MathUtilities::mean(const vector<double> &src,
                            int start,
                            int count)
 {
@@ -197,7 +197,7 @@
 	return index;
 }
 
-int MathUtilities::getMax( const std::vector<double> & data, double* pMax )
+int MathUtilities::getMax( const vector<double> & data, double* pMax )
 {
 	int index = 0;
 	int i;
@@ -286,7 +286,7 @@
     }
 }
 
-void MathUtilities::normalise(std::vector<double> &data, NormaliseType type)
+void MathUtilities::normalise(vector<double> &data, NormaliseType type)
 {
     switch (type) {
 
@@ -317,20 +317,46 @@
     }
 }
 
-void MathUtilities::adaptiveThreshold(std::vector<double> &data)
+double MathUtilities::getLpNorm(const vector<double> &data, int p)
+{
+    double tot = 0.0;
+    for (int i = 0; i < int(data.size()); ++i) {
+        tot += abs(pow(data[i], p));
+    }
+    return pow(tot, 1.0 / p);
+}
+
+vector<double> MathUtilities::normaliseLp(const vector<double> &data,
+                                               int p,
+                                               double threshold)
+{
+    int n = int(data.size());
+    if (n == 0 || p == 0) return data;
+    double norm = getLpNorm(data, p);
+    if (norm < threshold) {
+        return vector<double>(n, 1.0 / pow(n, 1.0 / p)); // unit vector
+    }
+    vector<double> out(n);
+    for (int i = 0; i < n; ++i) {
+        out[i] = data[i] / norm;
+    }
+    return out;
+}
+    
+void MathUtilities::adaptiveThreshold(vector<double> &data)
 {
     int sz = int(data.size());
     if (sz == 0) return;
 
-    std::vector<double> smoothed(sz);
+    vector<double> smoothed(sz);
 	
     int p_pre = 8;
     int p_post = 7;
 
     for (int i = 0; i < sz; ++i) {
 
-        int first = std::max(0,      i - p_pre);
-        int last  = std::min(sz - 1, i + p_post);
+        int first = max(0,      i - p_pre);
+        int last  = min(sz - 1, i + p_post);
 
         smoothed[i] = mean(data, first, last - first + 1);
     }
--- a/maths/MathUtilities.h	Wed Oct 07 09:33:28 2015 +0100
+++ b/maths/MathUtilities.h	Wed Oct 07 11:15:13 2015 +0100
@@ -73,15 +73,22 @@
      */
     static double mod( double x, double y);
 
+    /**
+     * The alpha norm is the alpha'th root of the mean alpha'th power
+     * magnitude. For example if alpha = 2 this corresponds to the RMS
+     * of the input data, and when alpha = 1 this is the mean
+     * magnitude.
+     */
     static void	  getAlphaNorm(const double *data, int len, int alpha, double* ANorm);
+
+    /**
+     * The alpha norm is the alpha'th root of the mean alpha'th power
+     * magnitude. For example if alpha = 2 this corresponds to the RMS
+     * of the input data, and when alpha = 1 this is the mean
+     * magnitude.
+     */
     static double getAlphaNorm(const std::vector <double> &data, int alpha );
 
-    static void   circShift( double* data, int length, int shift);
-
-    static int	  getMax( double* data, int length, double* max = 0 );
-    static int	  getMax( const std::vector<double> &data, double* max = 0 );
-    static int    compareInt(const void * a, const void * b);
-
     enum NormaliseType {
         NormaliseNone,
         NormaliseUnitSum,
@@ -95,11 +102,34 @@
                           NormaliseType n = NormaliseUnitMax);
 
     /**
+     * Calculate the L^p norm of a vector. Equivalent to MATLAB's
+     * norm(data, p).
+     */
+    static double getLpNorm(const std::vector<double> &data,
+                            int p);
+
+    /**
+     * Normalise a vector by dividing through by its L^p norm. If the
+     * norm is below the given threshold, the unit vector for that
+     * norm is returned. p may be 0, in which case no normalisation
+     * happens and the data is returned unchanged.
+     */
+    static std::vector<double> normaliseLp(const std::vector<double> &data,
+                                           int p,
+                                           double threshold = 1e-6);
+    
+    /**
      * Threshold the input/output vector data against a moving-mean
      * average filter.
      */
     static void adaptiveThreshold(std::vector<double> &data);
 
+    static void   circShift( double* data, int length, int shift);
+
+    static int	  getMax( double* data, int length, double* max = 0 );
+    static int	  getMax( const std::vector<double> &data, double* max = 0 );
+    static int    compareInt(const void * a, const void * b);
+
     /** 
      * Return true if x is 2^n for some integer n >= 0.
      */
--- a/tests/Makefile	Wed Oct 07 09:33:28 2015 +0100
+++ b/tests/Makefile	Wed Oct 07 11:15:13 2015 +0100
@@ -1,11 +1,11 @@
 
 CFLAGS := -I.. $(CFLAGS)
-CXXFLAGS := -I.. -Wall -Wextra -g $(CXXFLAGS)
+CXXFLAGS := -I.. -Wall -Wextra -std=c++11 -g $(CXXFLAGS)
 
 LDFLAGS := $(LDFLAGS) -lboost_unit_test_framework -lpthread
 LIBS := ../libqm-dsp.a
 
-TESTS	:= test-mathutilities test-window test-fft test-pvoc test-resampler test-medianfilter
+TESTS	:= test-mathutilities test-window test-filter test-fft test-dct test-pvoc test-resampler test-medianfilter
 
 VG	:= valgrind -q
 
@@ -21,9 +21,15 @@
 test-window: TestWindow.o $(LIBS)
 	$(CXX) -o $@ $^ $(LDFLAGS)
 
+test-filter: TestFilter.o $(LIBS)
+	$(CXX) -o $@ $^ $(LDFLAGS)
+
 test-fft: TestFFT.o $(LIBS)
 	$(CXX) -o $@ $^ $(LDFLAGS)
 
+test-dct: TestDCT.o $(LIBS)
+	$(CXX) -o $@ $^ $(LDFLAGS)
+
 test-pvoc: TestPhaseVocoder.o $(LIBS)
 	$(CXX) -o $@ $^ $(LDFLAGS)
 
@@ -33,7 +39,9 @@
 TestMathUtilities.o: $(LIBS)
 TestMedianFilter.o: $(LIBS)
 TestWindow.o: $(LIBS)
+TestFilter.o: $(LIBS)
 TestFFT.o: $(LIBS)
+TestDCT.o: $(LIBS)
 TestPhaseVocoder.o: $(LIBS)
 TestResampler.o: $(LIBS)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/TestDCT.cpp	Wed Oct 07 11:15:13 2015 +0100
@@ -0,0 +1,196 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#include "dsp/transforms/DCT.h"
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MAIN
+
+#include <boost/test/unit_test.hpp>
+
+#include <stdexcept>
+
+using namespace std;
+
+BOOST_AUTO_TEST_SUITE(TestDCT)
+
+BOOST_AUTO_TEST_CASE(forwardArrayBounds)
+{
+    // initialise bins to something recognisable, so we can tell if
+    // they haven't been written; and allocate the inputs on the heap
+    // so that, if running under valgrind, we get warnings about
+    // overruns
+    double *in = new double[4];
+    in[0] = 1;
+    in[1] = 1;
+    in[2] = -1;
+    in[3] = -1;
+    double out[] = { 999, 999, 999, 999, 999, 999 };
+    DCT(4).forward(in, out+1);
+    // And check we haven't overrun the arrays
+    BOOST_CHECK_EQUAL(out[0], 999.0);
+    BOOST_CHECK_EQUAL(out[5], 999.0);
+    delete[] in;
+}
+
+BOOST_AUTO_TEST_CASE(u_forwardArrayBounds)
+{
+    // initialise bins to something recognisable, so we can tell if
+    // they haven't been written; and allocate the inputs on the heap
+    // so that, if running under valgrind, we get warnings about
+    // overruns
+    double *in = new double[4];
+    in[0] = 1;
+    in[1] = 1;
+    in[2] = -1;
+    in[3] = -1;
+    double out[] = { 999, 999, 999, 999, 999, 999 };
+    DCT(4).forwardUnitary(in, out+1);
+    // And check we haven't overrun the arrays
+    BOOST_CHECK_EQUAL(out[0], 999.0);
+    BOOST_CHECK_EQUAL(out[5], 999.0);
+    delete[] in;
+}
+
+BOOST_AUTO_TEST_CASE(inverseArrayBounds)
+{
+    // initialise bins to something recognisable, so we can tell if
+    // they haven't been written; and allocate the inputs on the heap
+    // so that, if running under valgrind, we get warnings about
+    // overruns
+    double *in = new double[4];
+    in[0] = 1;
+    in[1] = 1;
+    in[2] = -1;
+    in[3] = -1;
+    double out[] = { 999, 999, 999, 999, 999, 999 };
+    DCT(4).inverse(in, out+1);
+    // And check we haven't overrun the arrays
+    BOOST_CHECK_EQUAL(out[0], 999.0);
+    BOOST_CHECK_EQUAL(out[5], 999.0);
+    delete[] in;
+
+}
+
+BOOST_AUTO_TEST_CASE(u_inverseArrayBounds)
+{
+    // initialise bins to something recognisable, so we can tell if
+    // they haven't been written; and allocate the inputs on the heap
+    // so that, if running under valgrind, we get warnings about
+    // overruns
+    double *in = new double[4];
+    in[0] = 1;
+    in[1] = 1;
+    in[2] = -1;
+    in[3] = -1;
+    double out[] = { 999, 999, 999, 999, 999, 999 };
+    DCT(4).inverseUnitary(in, out+1);
+    // And check we haven't overrun the arrays
+    BOOST_CHECK_EQUAL(out[0], 999.0);
+    BOOST_CHECK_EQUAL(out[5], 999.0);
+    delete[] in;
+}
+
+BOOST_AUTO_TEST_CASE(nonU4)
+{
+    int n = 4;
+    vector<double> in { 1, 2, 3, 5 };
+    vector<double> out(n);
+    vector<double> inv(n);
+    vector<double> expected { 22.0, -8.1564, 1.4142, -1.2137 };
+    
+    DCT d(n);
+
+    // our expected values are very approximate
+    double thresh = 1e-4;
+
+    d.forward(in.data(), out.data());
+    
+    for (int i = 0; i < n; ++i) {
+	BOOST_CHECK_SMALL(expected[i] - out[i], thresh);
+    }
+
+    d.inverse(out.data(), inv.data());
+
+    for (int i = 0; i < n; ++i) {
+	BOOST_CHECK_SMALL(in[i] - inv[i], thresh);
+    }
+    
+    // do it again, just in case some internal state was modified in inverse
+    
+    d.forward(in.data(), out.data());
+    
+    for (int i = 0; i < n; ++i) {
+	BOOST_CHECK_SMALL(expected[i] - out[i], thresh);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(u4)
+{
+    int n = 4;
+    vector<double> in { 1, 2, 3, 5 };
+    vector<double> out(n);
+    vector<double> inv(n);
+    vector<double> expected { 5.5, -2.8837, 0.5, -0.4291 };
+    
+    DCT d(n);
+
+    // our expected values are very approximate
+    double thresh = 1e-4;
+
+    d.forwardUnitary(in.data(), out.data());
+    
+    for (int i = 0; i < n; ++i) {
+	BOOST_CHECK_SMALL(expected[i] - out[i], thresh);
+    }
+
+    d.inverseUnitary(out.data(), inv.data());
+
+    for (int i = 0; i < n; ++i) {
+	BOOST_CHECK_SMALL(in[i] - inv[i], thresh);
+    }
+    
+    // do it again, just in case some internal state was modified in inverse
+    
+    d.forwardUnitary(in.data(), out.data());
+    
+    for (int i = 0; i < n; ++i) {
+	BOOST_CHECK_SMALL(expected[i] - out[i], thresh);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(u5)
+{
+    int n = 5;
+    vector<double> in { 1, 2, 3, 5, 6 };
+    vector<double> out(n);
+    vector<double> inv(n);
+    vector<double> expected { 7.6026, -4.1227, 0.3162, -0.0542, -0.3162 };
+    
+    DCT d(n);
+
+    // our expected values are very approximate
+    double thresh = 1e-4;
+
+    d.forwardUnitary(in.data(), out.data());
+    
+    for (int i = 0; i < n; ++i) {
+	BOOST_CHECK_SMALL(expected[i] - out[i], thresh);
+    }
+
+    d.inverseUnitary(out.data(), inv.data());
+
+    for (int i = 0; i < n; ++i) {
+	BOOST_CHECK_SMALL(in[i] - inv[i], thresh);
+    }
+    
+    // do it again, just in case some internal state was modified in inverse
+    
+    d.forwardUnitary(in.data(), out.data());
+    
+    for (int i = 0; i < n; ++i) {
+	BOOST_CHECK_SMALL(expected[i] - out[i], thresh);
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/TestFilter.cpp	Wed Oct 07 11:15:13 2015 +0100
@@ -0,0 +1,96 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#include "dsp/signalconditioning/Filter.h"
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MAIN
+
+#include <boost/test/unit_test.hpp>
+
+#include <stdexcept>
+
+using namespace std;
+
+BOOST_AUTO_TEST_SUITE(TestFilter)
+
+static vector<double> in { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+
+static vector<double> iir_a { 1,5.75501989315662,16.326056867468,28.779190797823,34.2874379215653,28.137815126537,15.6064643257793,5.37874515231553,0.913800050254382,0.0,0.0 };
+static vector<double> iir_b { 0.0031954608137085,0.0180937089815597,0.0508407778575426,0.0895040074158415,0.107385387168148,0.0895040074158415,0.0508407778575426,0.0180937089815597,0.0031954608137085,0.0,0.0 };
+static vector<double> iir_expected { 0.003195460813709, 0.006094690058282, 0.009370240771381, 0.012857578361690, 0.015328760300750, 0.019107809614909, 0.022257958968869, 0.024598034053011, 0.029106103380941, 0.031152166476509, 0.034424013713795, 0.038775350541015, 0.039924063374886, 0.044846280036012, 0.047614917256999, 0.049338485830505 };
+
+static vector<double> fir_b { -1.5511e-18,-0.022664,1.047e-17,0.27398,0.49737,0.27398,1.047e-17,-0.022664,-1.5511e-18 };
+static vector<double> fir_expected { -1.5511e-18,-0.022664,-0.045328,0.20599,0.95467,1.9773,3,4,5,6,7,8,9,10,11,12 };
+
+BOOST_AUTO_TEST_CASE(iir)
+{
+    vector<double> a(iir_a);
+    vector<double> b(iir_b);
+    vector<double> expected(iir_expected);
+
+    Filter f({ a, b });
+    
+    int n = expected.size();
+    vector<double> out(n, 0.0);
+
+    f.process(in.data(), out.data(), n);
+    
+    double thresh = 1e-12;
+    
+    for (int i = 0; i < n; ++i) {
+	BOOST_CHECK_SMALL(out[i] - expected[i], thresh);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(iir_chunked)
+{
+    vector<double> a(iir_a);
+    vector<double> b(iir_b);
+    vector<double> expected(iir_expected);
+
+    Filter f({ a, b });
+    
+    int n = expected.size();
+    vector<double> out(n, 0.0);
+
+    int j = 0;
+    int i = 0;
+    while (j < n) {
+	if (++i == 4) {
+	    i = 1;
+	}
+	if (j + i >= n) {
+	    i = n - j;
+	}
+	f.process(in.data() + j, out.data() + j, i);
+	j += i;
+    }
+    
+    double thresh = 1e-12;
+    
+    for (int i = 0; i < n; ++i) {
+	BOOST_CHECK_SMALL(out[i] - expected[i], thresh);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(fir)
+{
+    vector<double> b(fir_b);
+    vector<double> expected(fir_expected);
+
+    Filter f({ {}, b });
+    
+    int n = expected.size();
+    vector<double> out(n, 0.0);
+
+    f.process(in.data(), out.data(), n);
+    
+    double thresh = 1e-4;
+    
+    for (int i = 0; i < n; ++i) {
+	BOOST_CHECK_SMALL(out[i] - expected[i], thresh);
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
--- a/tests/TestMathUtilities.cpp	Wed Oct 07 09:33:28 2015 +0100
+++ b/tests/TestMathUtilities.cpp	Wed Oct 07 11:15:13 2015 +0100
@@ -3,12 +3,15 @@
 #include "maths/MathUtilities.h"
 
 #include <cmath>
+#include <iostream>
 
 #define BOOST_TEST_DYN_LINK
 #define BOOST_TEST_MAIN
 
 #include <boost/test/unit_test.hpp>
 
+using namespace std;
+
 BOOST_AUTO_TEST_SUITE(TestMathUtilities)
 
 BOOST_AUTO_TEST_CASE(round)
@@ -34,7 +37,7 @@
     BOOST_CHECK_EQUAL(MathUtilities::mean(d0, 4), 1.5);
     double d1[] = { -2.6 };
     BOOST_CHECK_EQUAL(MathUtilities::mean(d1, 1), -2.6);
-    std::vector<double> v;
+    vector<double> v;
     v.push_back(0);
     v.push_back(4);
     v.push_back(3);
@@ -148,6 +151,98 @@
     BOOST_CHECK_EQUAL(MathUtilities::gcd(37, 18), 1);
 }
 
+BOOST_AUTO_TEST_CASE(getAlphaNorm1)
+{
+    vector<double> in { -1, 1.5, 3, 5 };
+    double out = MathUtilities::getAlphaNorm(in, 1);
+    double expected = 2.6250;
+    double thresh = 1e-4;
+    BOOST_CHECK_SMALL(out - expected, thresh);
+}
+
+BOOST_AUTO_TEST_CASE(getAlphaNorm2)
+{
+    vector<double> in { -1, 1.5, 3, 5 };
+    double out = MathUtilities::getAlphaNorm(in, 2);
+    double expected = 3.0516;
+    double thresh = 1e-4;
+    BOOST_CHECK_SMALL(out - expected, thresh);
+}
+
+BOOST_AUTO_TEST_CASE(getL1Norm)
+{
+    vector<double> in { -1, 1.5, 3, 5 };
+    double out = MathUtilities::getLpNorm(in, 1);
+    // L1 norm is the sum of magnitudes
+    double expected = 10.5;
+    double thresh = 1e-5;
+    BOOST_CHECK_SMALL(out - expected, thresh);
+}
+
+BOOST_AUTO_TEST_CASE(getL2Norm)
+{
+    vector<double> in { -1, 1.5, 3, 5 };
+    double out = MathUtilities::getLpNorm(in, 2);
+    // L2 norm is the sqrt of sum of squared magnitudes
+    double expected = sqrt(37.25);
+    double thresh = 1e-5;
+    BOOST_CHECK_SMALL(out - expected, thresh);
+}
+
+BOOST_AUTO_TEST_CASE(getL3Norm)
+{
+    vector<double> in { -1, 1.5, 3, 5 };
+    double out = MathUtilities::getLpNorm(in, 3);
+    double expected = 5.3875;
+    double thresh = 1e-4;
+    BOOST_CHECK_SMALL(out - expected, thresh);
+}
+
+BOOST_AUTO_TEST_CASE(normaliseL1)
+{
+    vector<double> in { -1, 1.5, 3, 5 };
+    vector<double> expected { -0.095238, 0.142857, 0.285714, 0.476190 };
+    vector<double> out = MathUtilities::normaliseLp(in, 1);
+    double thresh = 1e-5;
+    for (int i = 0; i < int(out.size()); ++i) {
+        BOOST_CHECK_SMALL(out[i] - expected[i], thresh);
+    }
+    out = MathUtilities::normaliseLp({ 0, 0, 0, 0 }, 1);
+    for (int i = 0; i < int(out.size()); ++i) {
+        BOOST_CHECK_EQUAL(out[i], 0.25);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(normaliseL2)
+{
+    vector<double> in { -1, 1.5, 3, 5 };
+    vector<double> expected { -0.16385, 0.24577, 0.49154, 0.81923 };
+    vector<double> out = MathUtilities::normaliseLp(in, 2);
+    double thresh = 1e-5;
+    for (int i = 0; i < int(out.size()); ++i) {
+        BOOST_CHECK_SMALL(out[i] - expected[i], thresh);
+    }
+    out = MathUtilities::normaliseLp({ 0, 0, 0, 0 }, 2);
+    for (int i = 0; i < int(out.size()); ++i) {
+        BOOST_CHECK_EQUAL(out[i], 0.5);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(normaliseL3)
+{
+    vector<double> in { -1, 1.5, 3, 5 };
+    vector<double> expected { -0.18561, 0.27842, 0.55684, 0.92807 };
+    vector<double> out = MathUtilities::normaliseLp(in, 3);
+    double thresh = 1e-5;
+    for (int i = 0; i < int(out.size()); ++i) {
+        BOOST_CHECK_SMALL(out[i] - expected[i], thresh);
+    }
+    out = MathUtilities::normaliseLp({ 0, 0, 0, 0 }, 3);
+    for (int i = 0; i < int(out.size()); ++i) {
+        BOOST_CHECK_SMALL(out[i] - 0.62996, 1e-5);
+    }
+}
+
 BOOST_AUTO_TEST_SUITE_END()