Mercurial > hg > silvet
changeset 32:da54468cc452
Filter the constant Q spectrogram in a similar manner to the matlab version
author | Chris Cannam |
---|---|
date | Fri, 04 Apr 2014 13:29:33 +0100 |
parents | c6d230c31713 |
children | e08c330a761d |
files | .hgsubstate Makefile.inc src/Silvet.cpp src/Silvet.h |
diffstat | 4 files changed, 162 insertions(+), 12 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgsubstate Thu Apr 03 17:38:45 2014 +0100 +++ b/.hgsubstate Fri Apr 04 13:29:33 2014 +0100 @@ -1,1 +1,1 @@ -320aa9b3a2de3501a8e999319ad49862354416a1 constant-q-cpp +ac0617538cf86fe83ffb8e352d7d04a2d7b9bb99 constant-q-cpp
--- a/Makefile.inc Thu Apr 03 17:38:45 2014 +0100 +++ b/Makefile.inc Fri Apr 04 13:29:33 2014 +0100 @@ -22,8 +22,8 @@ VAMP_HEADERS := $(SRC_DIR)/Silvet.h VAMP_SOURCES := $(SRC_DIR)/Silvet.cpp $(SRC_DIR)/libmain.cpp -CQ_HEADERS := $(CQ_DIR)/CQKernel.h $(CQ_DIR)/ConstantQ.h -CQ_SOURCES := $(CQ_DIR)/CQKernel.cpp $(CQ_DIR)/ConstantQ.cpp +CQ_HEADERS := $(CQ_DIR)/CQKernel.h $(CQ_DIR)/ConstantQ.h $(CQ_DIR)/CQInterpolated.h +CQ_SOURCES := $(CQ_DIR)/CQKernel.cpp $(CQ_DIR)/ConstantQ.cpp $(CQ_DIR)/CQInterpolated.cpp HEADERS := $(VAMP_HEADERS) $(CQ_HEADERS) SOURCES := $(VAMP_SOURCES) $(CQ_SOURCES) @@ -47,3 +47,23 @@ # DO NOT DELETE +src/Silvet.o: src/Silvet.h data/include/templates.h data/include/bassoon.h +src/Silvet.o: data/include/cello.h data/include/clarinet.h +src/Silvet.o: data/include/flute.h data/include/guitar.h data/include/horn.h +src/Silvet.o: data/include/oboe.h data/include/tenorsax.h +src/Silvet.o: data/include/violin.h data/include/piano-maps-SptkBGCl.h +src/Silvet.o: data/include/piano1.h data/include/piano2.h +src/Silvet.o: data/include/piano3.h +src/Silvet.o: constant-q-cpp/cpp-qm-dsp/CQInterpolated.h +src/Silvet.o: constant-q-cpp/cpp-qm-dsp/ConstantQ.h +src/Silvet.o: constant-q-cpp/cpp-qm-dsp/CQKernel.h +src/libmain.o: src/Silvet.h +constant-q-cpp/cpp-qm-dsp/CQKernel.o: constant-q-cpp/cpp-qm-dsp/CQKernel.h +constant-q-cpp/cpp-qm-dsp/ConstantQ.o: constant-q-cpp/cpp-qm-dsp/ConstantQ.h +constant-q-cpp/cpp-qm-dsp/ConstantQ.o: constant-q-cpp/cpp-qm-dsp/CQKernel.h +constant-q-cpp/cpp-qm-dsp/CQInterpolated.o: constant-q-cpp/cpp-qm-dsp/CQInterpolated.h +constant-q-cpp/cpp-qm-dsp/CQInterpolated.o: constant-q-cpp/cpp-qm-dsp/ConstantQ.h +constant-q-cpp/cpp-qm-dsp/CQInterpolated.o: constant-q-cpp/cpp-qm-dsp/CQKernel.h +constant-q-cpp/cpp-qm-dsp/ConstantQ.o: constant-q-cpp/cpp-qm-dsp/CQKernel.h +constant-q-cpp/cpp-qm-dsp/CQInterpolated.o: constant-q-cpp/cpp-qm-dsp/ConstantQ.h +constant-q-cpp/cpp-qm-dsp/CQInterpolated.o: constant-q-cpp/cpp-qm-dsp/CQKernel.h
--- a/src/Silvet.cpp Thu Apr 03 17:38:45 2014 +0100 +++ b/src/Silvet.cpp Fri Apr 04 13:29:33 2014 +0100 @@ -17,19 +17,22 @@ #include "data/include/templates.h" +#include "maths/MedianFilter.h" #include "dsp/rateconversion/Resampler.h" -#include "constant-q-cpp/cpp-qm-dsp/ConstantQ.h" +#include "constant-q-cpp/cpp-qm-dsp/CQInterpolated.h" #include <vector> +#include <cstdio> + using std::vector; using std::cerr; using std::endl; static int processingSampleRate = 44100; static int processingBPO = 60; - +static int processingHeight = 545; Silvet::Silvet(float inputSampleRate) : Plugin(inputSampleRate), @@ -42,6 +45,10 @@ { delete m_resampler; delete m_cq; + for (int i = 0; i < (int)m_filterA.size(); ++i) { + delete m_filterA[i]; + delete m_filterB[i]; + } } string @@ -169,8 +176,32 @@ d.hasKnownExtents = false; d.isQuantized = false; d.sampleType = OutputDescriptor::VariableSampleRate; - d.sampleRate = 0; + d.sampleRate = m_inputSampleRate / (m_cq ? m_cq->getColumnHop() : 256); d.hasDuration = true; + m_notesOutputNo = list.size(); + list.push_back(d); + + d.identifier = "inputgrid"; + d.name = "Filtered time-frequency grid"; + d.description = "The pre-processed constant-Q time-frequency distribution used as input to the PLCA step"; + d.unit = ""; + d.hasFixedBinCount = true; + d.binCount = processingHeight; + d.binNames.clear(); + if (m_cq) { + char name[20]; + for (int i = 0; i < processingHeight; ++i) { + float freq = m_cq->getBinFrequency(i + 55); + sprintf(name, "%.1f Hz", freq); + d.binNames.push_back(name); + } + } + d.hasKnownExtents = false; + d.isQuantized = false; + d.sampleType = OutputDescriptor::FixedSampleRate; + d.sampleRate = 25; + d.hasDuration = false; + m_cqOutputNo = list.size(); list.push_back(d); return list; @@ -207,9 +238,22 @@ m_resampler = 0; } - m_cq = new ConstantQ - (processingSampleRate, 27.5, processingSampleRate / 3, processingBPO); + m_cq = new CQInterpolated + (processingSampleRate, 27.5, processingSampleRate / 3, processingBPO, + CQInterpolated::Linear); + for (int i = 0; i < (int)m_filterA.size(); ++i) { + delete m_filterA[i]; + delete m_filterB[i]; + } + m_filterA.clear(); + m_filterB.clear(); + for (int i = 0; i < processingHeight; ++i) { + m_filterA.push_back(new MedianFilter<double>(40)); + m_filterB.push_back(new MedianFilter<double>(40)); + } + m_columnCount = 0; + m_reducedColumnCount = 0; } Silvet::FeatureSet @@ -222,9 +266,20 @@ data = m_resampler->process(data.data(), data.size()); } - vector<vector<double> > cqout = m_cq->process(data); + Grid cqout = m_cq->process(data); + Grid filtered = preProcess(cqout); - return FeatureSet(); + FeatureSet fs; + + for (int i = 0; i < (int)filtered.size(); ++i) { + Feature f; + for (int j = 0; j < processingHeight; ++j) { + f.values.push_back(float(filtered[i][j])); + } + fs[m_cqOutputNo].push_back(f); + } + + return fs; } Silvet::FeatureSet @@ -234,3 +289,61 @@ return FeatureSet(); } +Silvet::Grid +Silvet::preProcess(const Grid &in) +{ + int width = in.size(); + + // reduce to 100 columns per second, or one column every 441 samples + + int spacing = processingSampleRate / 100; + + Grid out; + + for (int i = 0; i < width; ++i) { + + int prevSampleNo = (m_columnCount - 1) * m_cq->getColumnHop(); + int sampleNo = m_columnCount * m_cq->getColumnHop(); + + bool select = (sampleNo / spacing != prevSampleNo / spacing); + + if (select) { + vector<double> inCol = in[i]; + vector<double> outCol(processingHeight); + + // we reverse the column as we go (the CQ output is + // "upside-down", with high frequencies at the start of + // each column, and we want it the other way around) and + // then ignore the first 55 (lowest-frequency) bins, + // giving us 545 bins instead of 600 + + for (int j = 0; j < processingHeight; ++j) { + + int ix = inCol.size() - j - 55; + + double val = inCol[ix]; + m_filterA[j]->push(val); + + double a = m_filterA[j]->get(); + m_filterB[j]->push(std::min(a, val)); + + double filtered = m_filterB[j]->get(); + outCol[j] = filtered; + } + + // then we only use every fourth filtered column, for 25 + // columns per second in the eventual grid + + if (m_reducedColumnCount % 4 == 0) { + out.push_back(outCol); + } + + ++m_reducedColumnCount; + } + + ++m_columnCount; + } + + return out; +} +
--- a/src/Silvet.h Thu Apr 03 17:38:45 2014 +0100 +++ b/src/Silvet.h Fri Apr 04 13:29:33 2014 +0100 @@ -18,10 +18,16 @@ #include <vamp-sdk/Plugin.h> +#include <vector> +#include <string> + +#include "maths/MedianFilter.h" + using std::string; +using std::vector; class Resampler; -class ConstantQ; +class CQInterpolated; class Silvet : public Vamp::Plugin { @@ -62,9 +68,20 @@ protected: Resampler *m_resampler; - ConstantQ *m_cq; + CQInterpolated *m_cq; + + typedef vector<vector<double> > Grid; + + vector<MedianFilter<double> *> m_filterA; + vector<MedianFilter<double> *> m_filterB; + Grid preProcess(const Grid &); int m_blockSize; + int m_columnCount; + int m_reducedColumnCount; + + mutable int m_notesOutputNo; + mutable int m_cqOutputNo; }; #endif