# HG changeset patch # User Chris Cannam # Date 1347377867 -3600 # Node ID f72a470fe4b543d25d24209923c95c02d18adaf1 # Parent c666067fb8dadbce6da15932946d9fc4aeefe997 Pull out mean filter, test it diff -r c666067fb8da -r f72a470fe4b5 CepstralPitchTracker.cpp --- a/CepstralPitchTracker.cpp Wed Aug 15 14:53:28 2012 +0100 +++ b/CepstralPitchTracker.cpp Tue Sep 11 16:37:47 2012 +0100 @@ -23,6 +23,7 @@ */ #include "CepstralPitchTracker.h" +#include "MeanFilter.h" #include "vamp-sdk/FFT.h" @@ -256,24 +257,6 @@ fs[1].push_back(nf); } -void -CepstralPitchTracker::filter(const double *cep, double *data) -{ - for (int i = 0; i < m_bins; ++i) { - double v = 0; - int n = 0; - // average according to the vertical filter length - for (int j = -m_vflen/2; j <= m_vflen/2; ++j) { - int ix = i + m_binFrom + j; - if (ix >= 0 && ix < (int)m_blockSize) { - v += cep[ix]; - ++n; - } - } - data[i] = v / n; - } -} - double CepstralPitchTracker::cubicInterpolate(const double y[4], double x) { @@ -380,7 +363,7 @@ int n = m_bins; double *data = new double[n]; - filter(rawcep, data); + MeanFilter(m_vflen).filterSubsequence(rawcep, data, m_blockSize, n, m_binFrom); delete[] rawcep; double maxval = 0.0; diff -r c666067fb8da -r f72a470fe4b5 Makefile.inc --- a/Makefile.inc Wed Aug 15 14:53:28 2012 +0100 +++ b/Makefile.inc Tue Sep 11 16:37:47 2012 +0100 @@ -14,6 +14,7 @@ PLUGIN := cepstral-pitchtracker$(PLUGIN_EXT) HEADERS := CepstralPitchTracker.h \ + MeanFilter.h \ NoteHypothesis.h \ PeakInterpolator.h @@ -24,6 +25,7 @@ PLUGIN_MAIN := libmain.cpp TESTS := test/test-notehypothesis \ + test/test-meanfilter \ test/test-peakinterpolator OBJECTS := $(SOURCES:.cpp=.o) @@ -40,6 +42,9 @@ test/test-notehypothesis: test/TestNoteHypothesis.o $(OBJECTS) $(CXX) -o $@ $^ $(TEST_LDFLAGS) +test/test-meanfilter: test/TestMeanFilter.o $(OBJECTS) + $(CXX) -o $@ $^ $(TEST_LDFLAGS) + test/test-peakinterpolator: test/TestPeakInterpolator.o $(OBJECTS) $(CXX) -o $@ $^ $(TEST_LDFLAGS) diff -r c666067fb8da -r f72a470fe4b5 MeanFilter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MeanFilter.h Tue Sep 11 16:37:47 2012 +0100 @@ -0,0 +1,73 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ +/* + This file is Copyright (c) 2012 Chris Cannam + + 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 AUTHORS 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. +*/ + +#ifndef _MEAN_FILTER_H_ +#define _MEAN_FILTER_H_ + +class MeanFilter +{ +public: + /** + * Construct a non-causal mean filter with filter length flen, + * that replaces each sample N with the mean of samples + * [N-floor(F/2) .. N+floor(F/2)] where F is the filter length. + * Only odd F are supported. + */ + MeanFilter(int flen) : m_flen(flen) { } + ~MeanFilter() { } + + /** + * Filter the n samples in "in" and place the results in "out" + */ + void filter(const double *in, double *out, const int n) { + filterSubsequence(in, out, n, n, 0); + } + + /** + * Filter the n samples starting at the given offset in the + * m-element array "in" and place the results in "out" + */ + void filterSubsequence(const double *in, double *out, + const int m, const int n, + const int offset) { + int half = m_flen/2; + for (int i = 0; i < n; ++i) { + double v = 0; + int n = 0; + for (int j = -half; j <= half; ++j) { + int ix = i + j + offset; + if (ix >= 0 && ix < m) { + v += in[ix]; + ++n; + } + } + out[i] = v / n; + } + } + +private: + int m_flen; +}; + +#endif diff -r c666067fb8da -r f72a470fe4b5 test/TestMeanFilter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/TestMeanFilter.cpp Tue Sep 11 16:37:47 2012 +0100 @@ -0,0 +1,91 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ +/* + This file is Copyright (c) 2012 Chris Cannam + + 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 AUTHORS 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. +*/ + +#include "MeanFilter.h" + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MAIN + +#include + +BOOST_AUTO_TEST_SUITE(TestMeanFilter) + +BOOST_AUTO_TEST_CASE(simpleFilter) +{ + double in[] = { 1.0, 2.0, 3.0 }; + double out[3]; + MeanFilter(3).filter(in, out, 3); + BOOST_CHECK_EQUAL(out[0], 1.5); + BOOST_CHECK_EQUAL(out[1], 2.0); + BOOST_CHECK_EQUAL(out[2], 2.5); +} + +BOOST_AUTO_TEST_CASE(simpleSubset) +{ + double in[] = { 1.0, 2.0, 3.0, 4.0, 5.0 }; + double out[3]; + MeanFilter(3).filterSubsequence(in, out, 5, 3, 1); + BOOST_CHECK_EQUAL(out[0], 2.0); + BOOST_CHECK_EQUAL(out[1], 3.0); + BOOST_CHECK_EQUAL(out[2], 4.0); +} + +BOOST_AUTO_TEST_CASE(flenExceedsArraySize) +{ + double in[] = { 1.0, 2.0, 3.0 }; + double out[3]; + MeanFilter(5).filter(in, out, 3); + BOOST_CHECK_EQUAL(out[0], 2.0); + BOOST_CHECK_EQUAL(out[1], 2.0); + BOOST_CHECK_EQUAL(out[2], 2.0); +} + +BOOST_AUTO_TEST_CASE(flenIs1) +{ + double in[] = { 1.0, 2.0, 3.0 }; + double out[3]; + MeanFilter(1).filter(in, out, 3); + BOOST_CHECK_EQUAL(out[0], in[0]); + BOOST_CHECK_EQUAL(out[1], in[1]); + BOOST_CHECK_EQUAL(out[2], in[2]); +} + +BOOST_AUTO_TEST_CASE(arraySizeIs1) +{ + double in[] = { 1.0 }; + double out[1]; + MeanFilter(3).filter(in, out, 1); + BOOST_CHECK_EQUAL(out[0], in[0]); +} + +BOOST_AUTO_TEST_CASE(subsequenceLengthIs1) +{ + double in[] = { 1.0, 2.0, 3.0 }; + double out[1]; + MeanFilter(3).filterSubsequence(in, out, 3, 1, 2); + BOOST_CHECK_EQUAL(out[0], 2.5); +} + +BOOST_AUTO_TEST_SUITE_END() +