# HG changeset patch # User Chris Cannam # Date 1443540983 -3600 # Node ID 353e88e4ebea286e82ee10f5ea661e3ca2209d61 # Parent 7330e78bedb41ddfa4f21c5109031d28209c0984 Refactor diff -r 7330e78bedb4 -r 353e88e4ebea Makefile.inc --- 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 diff -r 7330e78bedb4 -r 353e88e4ebea src/CRP.cpp --- 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 #include @@ -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; } diff -r 7330e78bedb4 -r 353e88e4ebea src/CRP.h --- 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 diff -r 7330e78bedb4 -r 353e88e4ebea src/DCTReduce.h --- /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 + +class DCTReduce +{ +public: + DCTReduce(int size, int coefficientsToDrop) : + m_size(size), + m_dct(size), + m_dctOut(size), + m_coefficientsToDrop(coefficientsToDrop) + { } + + std::vector process(std::vector in) { + std::vector 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 m_dctOut; + int m_coefficientsToDrop; +}; + +#endif + diff -r 7330e78bedb4 -r 353e88e4ebea src/LogCompress.h --- /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 +#include + +class LogCompress +{ +public: + static std::vector process(std::vector in, + double factor, + double addTerm) { + std::vector out; + out.reserve(in.size()); + for (auto x: in) { + out.push_back(log10(addTerm + factor * x)); + } + return std::move(out); + } +}; + +#endif + diff -r 7330e78bedb4 -r 353e88e4ebea src/OctaveFold.h --- /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 + +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 process(std::vector in) { + std::vector out(12, 0.0); + for (int i = 0; i < int(in.size()); ++i) { + out[(21+i) % 12] += in[i]; + } + return std::move(out); + } +}; + +#endif + diff -r 7330e78bedb4 -r 353e88e4ebea test/examples/README.txt --- /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. +