# HG changeset patch # User Chris Cannam # Date 1396608593 -3600 # Node ID f4fb0ac6120aeed01f82966753b396cfe94b13ba # Parent c2e9c91ed3f70b5d4bda45095b96cde45b9acebd Interpolation for CQ. But this is wrong, it is interpolating between points in the nearest full-height columns even where there is a nearer point in the not-full-height ones diff -r c2e9c91ed3f7 -r f4fb0ac6120a .hgignore --- a/.hgignore Mon Mar 24 16:30:40 2014 +0000 +++ b/.hgignore Fri Apr 04 11:49:53 2014 +0100 @@ -3,3 +3,4 @@ *.o *.so cpp-qm-dsp/test +*.class diff -r c2e9c91ed3f7 -r f4fb0ac6120a Makefile.inc --- a/Makefile.inc Mon Mar 24 16:30:40 2014 +0000 +++ b/Makefile.inc Fri Apr 04 11:49:53 2014 +0100 @@ -20,8 +20,8 @@ PLUGIN := cqvamp$(PLUGIN_EXT) TEST := $(LIB_DIR)/test -LIB_HEADERS := $(LIB_DIR)/CQKernel.h $(LIB_DIR)/ConstantQ.h -LIB_SOURCES := $(LIB_DIR)/CQKernel.cpp $(LIB_DIR)/ConstantQ.cpp +LIB_HEADERS := $(LIB_DIR)/CQKernel.h $(LIB_DIR)/ConstantQ.h $(LIB_DIR)/CQInterpolated.h +LIB_SOURCES := $(LIB_DIR)/CQKernel.cpp $(LIB_DIR)/ConstantQ.cpp $(LIB_DIR)/CQInterpolated.cpp VAMP_HEADERS := $(VAMP_DIR)/CQVamp.h VAMP_SOURCES := $(VAMP_DIR)/CQVamp.cpp $(VAMP_DIR)/libmain.cpp @@ -56,6 +56,11 @@ cpp-qm-dsp/CQKernel.o: cpp-qm-dsp/CQKernel.h cpp-qm-dsp/ConstantQ.o: cpp-qm-dsp/ConstantQ.h cpp-qm-dsp/CQKernel.h -vamp/CQVamp.o: vamp/CQVamp.h cpp-qm-dsp/ConstantQ.h cpp-qm-dsp/CQKernel.h -vamp/libmain.o: vamp/CQVamp.h +vamp/CQVamp.o: vamp/CQVamp.h cpp-qm-dsp/CQInterpolated.h +vamp/CQVamp.o: cpp-qm-dsp/ConstantQ.h cpp-qm-dsp/CQKernel.h +vamp/libmain.o: vamp/CQVamp.h cpp-qm-dsp/CQInterpolated.h +vamp/libmain.o: cpp-qm-dsp/ConstantQ.h cpp-qm-dsp/CQKernel.h cpp-qm-dsp/ConstantQ.o: cpp-qm-dsp/CQKernel.h +cpp-qm-dsp/CQInterpolated.o: cpp-qm-dsp/ConstantQ.h cpp-qm-dsp/CQKernel.h +vamp/CQVamp.o: cpp-qm-dsp/CQInterpolated.h cpp-qm-dsp/ConstantQ.h +vamp/CQVamp.o: cpp-qm-dsp/CQKernel.h diff -r c2e9c91ed3f7 -r f4fb0ac6120a Makefile.linux --- a/Makefile.linux Mon Mar 24 16:30:40 2014 +0000 +++ b/Makefile.linux Fri Apr 04 11:49:53 2014 +0100 @@ -1,6 +1,6 @@ -CFLAGS := -Wall -O3 -fPIC -I../vamp-plugin-sdk/ -#CFLAGS := -g -fPIC -I../vamp-plugin-sdk +#CFLAGS := -Wall -O3 -fPIC -I../vamp-plugin-sdk/ +CFLAGS := -g -fPIC -I../vamp-plugin-sdk CXXFLAGS := $(CFLAGS) diff -r c2e9c91ed3f7 -r f4fb0ac6120a cpp-qm-dsp/CQInterpolated.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cpp-qm-dsp/CQInterpolated.cpp Fri Apr 04 11:49:53 2014 +0100 @@ -0,0 +1,215 @@ +/* -*- 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. +*/ + +#include "CQInterpolated.h" + +#include + +using std::vector; + +using std::cerr; +using std::endl; + +CQInterpolated::CQInterpolated(double sampleRate, + double minFreq, double maxFreq, + int binsPerOctave, + Interpolation interpolation) : + m_cq(sampleRate, minFreq, maxFreq, binsPerOctave), + m_interpolation(interpolation) +{ +} + +CQInterpolated::~CQInterpolated() +{ +} + +vector > +CQInterpolated::process(const vector &td) +{ + return postProcess(m_cq.process(td), false); +} + +vector > +CQInterpolated::getRemainingBlocks() +{ + return postProcess(m_cq.getRemainingBlocks(), true); +} + +vector > +CQInterpolated::postProcess(vector > cq, bool insist) +{ + if (m_interpolation == None) { + return cq; + } + + int width = cq.size(); + + for (int i = 0; i < width; ++i) { + m_buffer.push_back(cq[i]); + } + + if (m_interpolation == Hold) { + return fetchHold(insist); + } else { + return fetchLinear(insist); + } +} + +vector > +CQInterpolated::fetchHold(bool) +{ + Grid out; + + int width = m_buffer.size(); + int height = getTotalBins(); + + for (int i = 0; i < width; ++i) { + + vector col = m_buffer[i]; + + int thisHeight = col.size(); + int prevHeight = m_prevColumn.size(); + + for (int j = thisHeight; j < height; ++j) { + if (j < prevHeight) { + col.push_back(m_prevColumn[j]); + } else { + col.push_back(0.0); + } + } + + m_prevColumn = col; + out.push_back(col); + } + + m_buffer.clear(); + + return out; +} + +vector > +CQInterpolated::fetchLinear(bool insist) +{ + Grid out; + + //!!! This is surprisingly messy. I must be missing something. + + // We can only return any data when we have at least one column + // that has the full height in the buffer, that is not the first + // column. + // + // If the first col has full height, and there is another one + // later that also does, then we can interpolate between those, up + // to but not including the second full height column. Then we + // drop and return the columns we interpolated, leaving the second + // full-height col as the first col in the buffer. And repeat as + // long as enough columns are available. + // + // If the first col does not have full height, then (so long as + // we're following the logic above) we must simply have not yet + // reached the first full-height column in the CQ output, and we + // can interpolate nothing. + + int width = m_buffer.size(); + int height = getTotalBins(); + + if (width == 0) return out; + + int firstFullHeight = -1; + int secondFullHeight = -1; + + for (int i = 0; i < width; ++i) { + if (m_buffer[i].size() == height) { + if (firstFullHeight == -1) { + firstFullHeight = i; + } else if (secondFullHeight == -1) { + secondFullHeight = i; + break; + } + } + } + + if (firstFullHeight < 0) { + if (insist) { + out = m_buffer; + m_buffer.clear(); + return out; + } else { + return out; + } + } else if (firstFullHeight > 0) { + // can interpolate nothing, stash up to first full height & recurse + out = Grid(m_buffer.begin(), m_buffer.begin() + firstFullHeight); + m_buffer = Grid(m_buffer.begin() + firstFullHeight, m_buffer.end()); + Grid more = fetchLinear(insist); + out.insert(out.end(), more.begin(), more.end()); + return out; + } else if (secondFullHeight < 0) { + // firstFullHeight == 0, but there is no second full height -- + // wait for it unless insist flag is set + if (insist) { + out = m_buffer; + m_buffer.clear(); + return out; + } else { + return out; + } + } else { + // firstFullHeight == 0 and secondFullHeight also valid. Can interpolate + + out.push_back(m_buffer[0]); + + for (int i = 1; i < secondFullHeight; ++i) { + + vector col = m_buffer[i]; + int thisHeight = col.size(); + + double proportion = double(i) / double(secondFullHeight); + + cerr << "secondFullHeight = " << secondFullHeight << " proportion = " << proportion << " "; + + for (int j = thisHeight; j < height; ++j) { + col.push_back((1.0 - proportion) * m_buffer[0][j] + + proportion * m_buffer[secondFullHeight][j]); + } + + out.push_back(col); + } + + m_buffer = Grid(m_buffer.begin() + secondFullHeight, m_buffer.end()); + Grid more = fetchLinear(insist); + out.insert(out.end(), more.begin(), more.end()); + return out; + } +} + + + diff -r c2e9c91ed3f7 -r f4fb0ac6120a cpp-qm-dsp/CQInterpolated.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cpp-qm-dsp/CQInterpolated.h Fri Apr 04 11:49:53 2014 +0100 @@ -0,0 +1,78 @@ +/* -*- 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_INTERPOLATED_H +#define CQ_INTERPOLATED_H + +#include +#include "ConstantQ.h" + +class CQInterpolated +{ +public: + enum Interpolation { + None, // leave empty cells empty + Hold, // repeat prior cell + Linear, // linear interpolation between consecutive time cells + }; + + CQInterpolated(double sampleRate, + double minFreq, double maxFreq, + int binsPerOctave, + Interpolation interpolation); + ~CQInterpolated(); + + double getSampleRate() const { return m_cq.getSampleRate(); } + int getBinsPerOctave() const { return m_cq.getBinsPerOctave(); } + int getOctaves() const { return m_cq.getOctaves(); } + int getTotalBins() const { return m_cq.getTotalBins(); } + int getColumnHop() const { return m_cq.getColumnHop(); } + int getLatency() const { return m_cq.getLatency(); } + double getMaxFrequency() const { return m_cq.getMaxFrequency(); } + double getMinFrequency() const { return m_cq.getMinFrequency(); } + double getBinFrequency(int bin) const { return m_cq.getBinFrequency(bin); } + + std::vector > process(const std::vector &); + std::vector > getRemainingBlocks(); + +private: + ConstantQ m_cq; + Interpolation m_interpolation; + + typedef std::vector > Grid; + Grid m_buffer; + Grid postProcess(Grid, bool insist); + Grid fetchHold(bool insist); + Grid fetchLinear(bool insist); + std::vector m_prevColumn; +}; + +#endif diff -r c2e9c91ed3f7 -r f4fb0ac6120a cpp-qm-dsp/ConstantQ.cpp --- a/cpp-qm-dsp/ConstantQ.cpp Mon Mar 24 16:30:40 2014 +0000 +++ b/cpp-qm-dsp/ConstantQ.cpp Fri Apr 04 11:49:53 2014 +0100 @@ -247,7 +247,7 @@ int base = out.size(); int totalColumns = pow(2, m_octaves - 1) * m_p.atomsPerFrame; for (int i = 0; i < totalColumns; ++i) { - out.push_back(vector(m_p.binsPerOctave * m_octaves, 0.0)); + out.push_back(vector()); } for (int octave = 0; octave < m_octaves; ++octave) { @@ -259,18 +259,20 @@ for (int j = 0; j < m_p.atomsPerFrame; ++j) { - for (int k = 0; k < pow(2, octave); ++k) { - - int target = base + k + + int target = base + (b * (totalColumns / blocksThisOctave) + (j * ((totalColumns / blocksThisOctave) / m_p.atomsPerFrame))); - for (int i = 0; i < m_p.binsPerOctave; ++i) { - out[target][m_p.binsPerOctave * octave + i] = - block[j][m_p.binsPerOctave - i - 1]; - } - } + while (out[target].size() < + m_p.binsPerOctave * (octave + 1)) { + out[target].push_back(0.0); + } + + for (int i = 0; i < m_p.binsPerOctave; ++i) { + out[target][m_p.binsPerOctave * octave + i] = + block[j][m_p.binsPerOctave - i - 1]; + } } } } diff -r c2e9c91ed3f7 -r f4fb0ac6120a vamp/CQVamp.cpp --- a/vamp/CQVamp.cpp Mon Mar 24 16:30:40 2014 +0000 +++ b/vamp/CQVamp.cpp Fri Apr 04 11:49:53 2014 +0100 @@ -31,8 +31,6 @@ #include "CQVamp.h" -#include "cpp-qm-dsp/ConstantQ.h" - #include "base/Pitch.h" #include @@ -49,6 +47,7 @@ m_maxMIDIPitch(84), m_tuningFrequency(440), m_bpo(24), + m_interpolation(CQInterpolated::Linear), m_cq(0), m_maxFrequency(inputSampleRate/2), m_minFrequency(46), @@ -147,6 +146,20 @@ desc.quantizeStep = 1; list.push_back(desc); + desc.identifier = "interpolation"; + desc.name = "Interpolation"; + desc.unit = ""; + desc.description = "Interpolation method used to fill empty cells in lower octaves"; + desc.minValue = 0; + desc.maxValue = 2; + desc.defaultValue = 2; + desc.isQuantized = true; + desc.quantizeStep = 1; + desc.valueNames.push_back("None, leave empty"); + desc.valueNames.push_back("None, repeat prior value"); + desc.valueNames.push_back("Linear interpolation"); + list.push_back(desc); + return list; } @@ -165,6 +178,9 @@ if (param == "bpo") { return m_bpo; } + if (param == "interpolation") { + return (float)m_interpolation; + } std::cerr << "WARNING: CQVamp::getParameter: unknown parameter \"" << param << "\"" << std::endl; return 0.0; @@ -179,8 +195,10 @@ m_maxMIDIPitch = lrintf(value); } else if (param == "tuning") { m_tuningFrequency = value; - } else if (param == "bpo") { + } else if (param == "bpo") { m_bpo = lrintf(value); + } else if (param == "interpolation") { + m_interpolation = (CQInterpolated::Interpolation)lrintf(value); } else { std::cerr << "WARNING: CQVamp::setParameter: unknown parameter \"" << param << "\"" << std::endl; @@ -192,7 +210,7 @@ { if (m_cq) { delete m_cq; - m_cq = 0; + m_cq = 0; } if (channels < getMinChannelCount() || @@ -206,8 +224,9 @@ m_maxFrequency = Pitch::getFrequencyForPitch (m_maxMIDIPitch, 0, m_tuningFrequency); - m_cq = new ConstantQ - (m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo); + m_cq = new CQInterpolated + (m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo, + m_interpolation); return true; } @@ -217,8 +236,9 @@ { if (m_cq) { delete m_cq; - m_cq = new ConstantQ - (m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo); + m_cq = new CQInterpolated + (m_inputSampleRate, m_minFrequency, m_maxFrequency, m_bpo, + m_interpolation); } m_prevFeature.clear(); m_haveStartTime = false; @@ -303,18 +323,16 @@ { FeatureSet returnFeatures; - for (int i = 0; i < (int)cqout.size(); ++i) { + int width = cqout.size(); + int height = m_cq->getTotalBins(); - vector column(m_cq->getTotalBins(), 0.f); + for (int i = 0; i < width; ++i) { - for (int j = 0; j < (int)cqout[i].size(); ++j) { + vector column(height, 0.f); + int thisHeight = cqout[i].size(); + for (int j = 0; j < thisHeight; ++j) { column[j] = cqout[i][j]; } - for (int j = cqout[i].size(); j < m_cq->getTotalBins(); ++j) { - if (j < (int)m_prevFeature.size()) { - column[j] = m_prevFeature[j]; - } - } // put low frequencies at the start std::reverse(column.begin(), column.end()); diff -r c2e9c91ed3f7 -r f4fb0ac6120a vamp/CQVamp.h --- a/vamp/CQVamp.h Mon Mar 24 16:30:40 2014 +0000 +++ b/vamp/CQVamp.h Fri Apr 04 11:49:53 2014 +0100 @@ -34,6 +34,8 @@ #include +#include "cpp-qm-dsp/CQInterpolated.h" + class ConstantQ; class CQVamp : public Vamp::Plugin @@ -73,8 +75,9 @@ int m_maxMIDIPitch; float m_tuningFrequency; int m_bpo; + CQInterpolated::Interpolation m_interpolation; - ConstantQ *m_cq; + CQInterpolated *m_cq; float m_maxFrequency; float m_minFrequency; int m_stepSize;