changeset 24:353e88e4ebea

Refactor
author Chris Cannam
date Tue, 29 Sep 2015 16:36:23 +0100
parents 7330e78bedb4
children 2c913b88b808
files Makefile.inc src/CRP.cpp src/CRP.h src/DCTReduce.h src/LogCompress.h src/OctaveFold.h test/examples/README.txt
diffstat 7 files changed, 174 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.inc	Tue Sep 29 12:43:07 2015 +0100
+++ b/Makefile.inc	Tue Sep 29 16:36:23 2015 +0100
@@ -24,7 +24,7 @@
 
 PUBLIC_HEADERS	:=
 
-LIB_HEADERS	:= $(SRC_DIR)/delays.h $(SRC_DIR)/filter-a.h $(SRC_DIR)/filter-b.h $(SRC_DIR)/Filter.h $(SRC_DIR)/PitchFilterbank.h $(SRC_DIR)/DCT.h $(SRC_DIR)/Types.h $(SRC_DIR)/CRP.h $(SRC_DIR)/Normalise.h
+LIB_HEADERS	:= $(SRC_DIR)/delays.h $(SRC_DIR)/filter-a.h $(SRC_DIR)/filter-b.h $(SRC_DIR)/Filter.h $(SRC_DIR)/PitchFilterbank.h $(SRC_DIR)/DCT.h $(SRC_DIR)/Types.h $(SRC_DIR)/CRP.h $(SRC_DIR)/Normalise.h $(SRC_DIR)/LogCompress.h $(SRC_DIR)/OctaveFold.h
 LIB_SOURCES	:= $(SRC_DIR)/Filter.cpp $(SRC_DIR)/PitchFilterbank.cpp $(SRC_DIR)/DCT.cpp $(SRC_DIR)/CRP.cpp $(SRC_DIR)/Normalise.cpp
 LIB_OBJECTS	:= $(LIB_SOURCES:.cpp=.o)
 LIB_OBJECTS	:= $(LIB_OBJECTS:.c=.o)
@@ -96,12 +96,21 @@
 src/Filter.o: src/Filter.h bqvec/bqvec/Restrict.h bqvec/bqvec/VectorOps.h
 src/Filter.o: bqvec/bqvec/Restrict.h bqvec/bqvec/Allocators.h
 src/Filter.o: bqvec/bqvec/VectorOps.h
-src/PitchFilterbank.o: src/PitchFilterbank.h src/Filter.h
+src/PitchFilterbank.o: src/PitchFilterbank.h src/Types.h src/Filter.h
 src/PitchFilterbank.o: bqvec/bqvec/Restrict.h src/delays.h src/filter-a.h
 src/PitchFilterbank.o: src/filter-b.h
-src/TipicVampPlugin.o: src/TipicVampPlugin.h src/PitchFilterbank.h
-src/TipicVampPlugin.o: bqvec/bqvec/Range.h bqvec/bqvec/VectorOps.h
-src/TipicVampPlugin.o: bqvec/bqvec/Restrict.h
-src/libmain.o: src/TipicVampPlugin.h src/PitchFilterbank.h
+src/DCT.o: src/DCT.h
+src/CRP.o: src/CRP.h src/Types.h src/DCT.h src/Normalise.h src/LogCompress.h
+src/CRP.o: src/OctaveFold.h src/DCTReduce.h
+src/Normalise.o: src/Normalise.h
+src/TipicVampPlugin.o: src/TipicVampPlugin.h src/Types.h
+src/TipicVampPlugin.o: src/PitchFilterbank.h src/CRP.h bqvec/bqvec/Range.h
+src/TipicVampPlugin.o: bqvec/bqvec/VectorOps.h bqvec/bqvec/Restrict.h
+src/libmain.o: src/TipicVampPlugin.h src/Types.h
+src/test-filter.o: src/Filter.h bqvec/bqvec/Restrict.h
+src/test-dct.o: src/DCT.h
+src/test-normalise.o: src/Normalise.h
 src/Filter.o: bqvec/bqvec/Restrict.h
-src/TipicVampPlugin.o: src/PitchFilterbank.h
+src/PitchFilterbank.o: src/Types.h
+src/CRP.o: src/Types.h
+src/TipicVampPlugin.o: src/Types.h
--- a/src/CRP.cpp	Tue Sep 29 12:43:07 2015 +0100
+++ b/src/CRP.cpp	Tue Sep 29 16:36:23 2015 +0100
@@ -4,6 +4,9 @@
 
 #include "DCT.h"
 #include "Normalise.h"
+#include "LogCompress.h"
+#include "OctaveFold.h"
+#include "DCTReduce.h"
 
 #include <cmath>
 #include <iostream>
@@ -12,7 +15,7 @@
 
 CRP::~CRP()
 {
-    delete m_dct;
+    delete m_dctReduce;
 }
 
 RealBlock
@@ -21,49 +24,24 @@
     if (in.empty()) {
 	return in;
     }
-    if (!m_dct) {
+    if (!m_dctReduce) {
 	m_size = in[0].size();
-	m_dct = new DCT(m_size);
+	m_dctReduce = new DCTReduce(m_size, m_params.coefficientsToDrop);
     }
 
-    int bins = 12;
     RealBlock out;
-    RealColumn dctOut(m_size);
-
-    int normP = 2; //!!! L2 norm (this is a param in MATLAB version)
 
     for (RealColumn col: in) {
 
-	RealColumn crp(bins);
-
-	if (int(col.size()) != m_size) {
-	    cerr << "ERROR: Inconsistent value count in pitch column: found "
-		 << col.size() << " where previous column(s) have had " << m_size
-		 << endl;
-	    throw std::logic_error("Inconsistent value count in pitch column");
-	}
-    
 	if (m_params.applyLogCompression) {
-	    for (auto &v: col) {
-		//!!! These numbers are parameters in the MATLAB
-		v = log10(1.0 + 1000.0 * v);
-	    }
+            col = LogCompress::process(col, m_params.logFactor, m_params.logAddTerm);
 	}
 
-	m_dct->forward(col.data(), dctOut.data());
-
-	for (int i = 0; i < m_params.coefficientsToDrop; ++i) {
-	    dctOut[i] = 0.0;
-	}
-
-	m_dct->inverse(dctOut.data(), col.data());
-
-	for (int i = 0; i < m_size; ++i) {
-	    crp[i % bins] += col[i];
-	}
-
-	out.push_back(Normalise::normalise(crp, normP));
-    }
+        out.push_back(Normalise::normalise
+                      (OctaveFold::process
+                       (m_dctReduce->process(col)),
+                       m_params.normP));
+    }        
 
     return out;
 }
--- a/src/CRP.h	Tue Sep 29 12:43:07 2015 +0100
+++ b/src/CRP.h	Tue Sep 29 16:36:23 2015 +0100
@@ -5,7 +5,7 @@
 
 #include "Types.h"
 
-class DCT;
+class DCTReduce;
 
 //!!! Downsampling/temporal smoothing not yet implemented
 
@@ -15,10 +15,19 @@
     struct Parameters {
 	int coefficientsToDrop;
 	bool applyLogCompression;
-	Parameters() : coefficientsToDrop(54), applyLogCompression(true) { }
+        double logFactor;
+        double logAddTerm;
+        int normP;
+	Parameters() :
+            coefficientsToDrop(54),
+            applyLogCompression(true),
+            logFactor(1000.0),
+            logAddTerm(1.0),
+            normP(2)
+        { }
     };
 	
-    CRP(Parameters params) : m_params(params), m_size(0), m_dct(0) { }
+    CRP(Parameters params) : m_params(params), m_size(0), m_dctReduce(0) { }
     ~CRP();
 
     RealBlock process(const RealBlock &in);
@@ -26,7 +35,7 @@
 private:
     Parameters m_params;
     int m_size;
-    DCT *m_dct;
+    DCTReduce *m_dctReduce;
 };
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/DCTReduce.h	Tue Sep 29 16:36:23 2015 +0100
@@ -0,0 +1,37 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#ifndef DCTREDUCE_H
+#define DCTREDUCE_H
+
+#include "DCT.h"
+#include <vector>
+
+class DCTReduce
+{
+public:
+    DCTReduce(int size, int coefficientsToDrop) :
+	m_size(size),
+	m_dct(size),
+	m_dctOut(size),
+	m_coefficientsToDrop(coefficientsToDrop)
+    { } 
+
+    std::vector<double> process(std::vector<double> in) {
+	std::vector<double> out(in.size());
+	m_dct.forward(in.data(), m_dctOut.data());
+	for (int i = 0; i < m_coefficientsToDrop && i < m_size; ++i) {
+	    m_dctOut[i] = 0.0;
+	}
+	m_dct.inverse(m_dctOut.data(), out.data());
+	return std::move(out);
+    }
+    
+private:
+    int m_size;
+    DCT m_dct;
+    std::vector<double> m_dctOut;
+    int m_coefficientsToDrop;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/LogCompress.h	Tue Sep 29 16:36:23 2015 +0100
@@ -0,0 +1,25 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#ifndef LOGCOMPRESS_H
+#define LOGCOMPRESS_H
+
+#include <vector>
+#include <cmath>
+
+class LogCompress
+{
+public:
+    static std::vector<double> process(std::vector<double> in,
+				       double factor,
+				       double addTerm) {
+	std::vector<double> out;
+	out.reserve(in.size());
+	for (auto x: in) {
+	    out.push_back(log10(addTerm + factor * x));
+	}
+	return std::move(out);
+    }
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/OctaveFold.h	Tue Sep 29 16:36:23 2015 +0100
@@ -0,0 +1,28 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#ifndef OCTAVEFOLD_H
+#define OCTAVEFOLD_H
+
+#include <vector>
+
+class OctaveFold
+{
+public:
+    /**
+     * Take an 88-bin pitch feature and sum it into a 12-bin
+     * octave. Each bin gets summed into chroma bin corresponding to
+     * its MIDI pitch modulo 12. The first 20 MIDI pitches are
+     * missing, so bin number n (for n from 0 to 87) is for MIDI pitch
+     * 21+n.
+     */
+    static std::vector<double> process(std::vector<double> in) {
+	std::vector<double> out(12, 0.0);
+	for (int i = 0; i < int(in.size()); ++i) {
+	    out[(21+i) % 12] += in[i];
+	}
+	return std::move(out);
+    }
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/examples/README.txt	Tue Sep 29 16:36:23 2015 +0100
@@ -0,0 +1,43 @@
+
+This directory contains some example data calculated using the MATLAB
+Chroma Toolbox.
+
+The file ylsf-30sec-tipic-pitch.csv contains our input data: 88-bin
+pitch features from a 30-second clip, at a rate of 10 per second so
+300 features total. These came from a test version of the Tipic
+plugin. They do not match the pitch features produced by the Chroma
+Toolbox, because Tipic uses a causal filterbank rather than the
+zero-phase forward-backward filter of the Chroma Toolbox.
+
+The remaining files contain the results of processing the
+ylsf-30sec-tipic-pitch.csv data using the pitch_to_* functions of the
+Chroma Toolbox, using MATLAB commands along the lines of
+
+>> pitch = csvread('ylsf-30sec-tipic-pitch.csv');
+>> chroma = pitch_to_chroma([zeros(20,300); pitch'; zeros(12,300)]);
+>> csvwrite('ylsf-30sec-chroma-from-tipic-pitch-defaults.csv', chroma');
+
+(The zeros are there because the Toolbox functions expect 120-bin
+pitch features while Tipic only emits 88 bins.)
+
+The files here are:
+
+ * ylsf-30sec-chroma-from-tipic-pitch-defaults.csv - CP chroma features,
+   default parameters
+
+ * ylsf-30sec-clp-from-tipic-pitch-defaults.csv - CLP log-compressed
+   chroma features, default parameters (apart from log compression)
+
+ * ylsf-30sec-cens-from-tipic-pitch.csv - CENS features, default parameters
+
+ * ylsf-30sec-crp-from-tipic-pitch-defaults.csv - CRP features, default
+   parameters
+
+ * ylsf-30sec-crp-from-tipic-pitch-downsample.csv - CRP features, 10x
+   downsampling with 41-point window enabled
+
+The proposition that these are intended to help test is: If Tipic
+produces the pitch features found in ylsf-30sec-tipic-pitch.csv, and
+if we assume the MATLAB implementations are correct, then Tipic should
+also produce chroma features matching those in these files.
+