Mercurial > hg > silvet
diff constant-q-cpp/test/TestCQFrequency.cpp @ 366:5d0a2ebb4d17
Bring dependent libraries in to repo
author | Chris Cannam |
---|---|
date | Fri, 24 Jun 2016 14:47:45 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/constant-q-cpp/test/TestCQFrequency.cpp Fri Jun 24 14:47:45 2016 +0100 @@ -0,0 +1,132 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#include "cq/CQSpectrogram.h" + +#include "dsp/Window.h" + +#include <cmath> +#include <vector> +#include <iostream> + +using std::vector; +using std::cerr; +using std::endl; + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MAIN + +#include <boost/test/unit_test.hpp> + +BOOST_AUTO_TEST_SUITE(TestCQFrequency) + +// The principle here is to feed a single windowed sinusoid into a +// small CQ transform and check that the output has its peak bin at +// the correct frequency. + +// Set up fs/2 = 50, frequency range 10 -> 40 i.e. 2 octaves, fixed +// duration of 2 seconds +static const double sampleRate = 100; +static const double cqmin = 11.8921; +static const double cqmax = 40; +static const double bpo = 4; +static const int duration = sampleRate * 2; + +// Threshold below which to ignore a column completely +static const double threshold = 0.08; + +int +binForFrequency(double freq) +{ + int bin = (bpo * 2) - round(bpo * log2(freq / cqmin)) - 1; + return bin; +} + +void +checkCQFreqColumn(int i, vector<double> column, + double freq, CQSpectrogram::Interpolation interp) +{ + double maxval = 0.0; + int maxidx = -1; + int height = column.size(); + + int nonZeroHeight = ((i % 2 == 1) ? height/2 : height); + + for (int j = 0; j < nonZeroHeight; ++j) { + if (j == 0 || column[j] > maxval) { + maxval = column[j]; + maxidx = j; + } + } + + int expected = binForFrequency(freq); + if (maxval < threshold) { + return; // ignore these columns at start and end + } else if (expected < nonZeroHeight && maxidx != expected) { + cerr << "ERROR: In column " << i << " with interpolation " << interp + << ", maximum value for frequency " << freq + << "\n found at index " << maxidx + << " (expected index " << expected << ")" << endl; + cerr << "column contains: "; + for (int j = 0; j < height; ++j) { + cerr << column[j] << " "; + } + cerr << endl; + BOOST_CHECK_EQUAL(maxidx, expected); + } +} + +void +testCQFrequencyWith(CQParameters params, + CQSpectrogram::Interpolation interp, + double freq) +{ + CQSpectrogram cq(params, interp); + + BOOST_CHECK_EQUAL(cq.getBinsPerOctave(), bpo); + BOOST_CHECK_EQUAL(cq.getOctaves(), 2); + BOOST_CHECK_CLOSE(cq.getBinFrequency(0), 40, 1e-10); + BOOST_CHECK_CLOSE(cq.getBinFrequency(4), 20, 1e-10); + BOOST_CHECK_CLOSE(cq.getBinFrequency(7), cqmin, 1e-3); + + vector<double> input; + for (int i = 0; i < duration; ++i) { + input.push_back(sin((i * 2 * M_PI * freq) / sampleRate)); + } + Window<double>(HanningWindow, duration).cut(input.data()); + + CQSpectrogram::RealBlock output = cq.process(input); + CQSpectrogram::RealBlock rest = cq.getRemainingOutput(); + output.insert(output.end(), rest.begin(), rest.end()); + + BOOST_CHECK_EQUAL(output[0].size(), + cq.getBinsPerOctave() * cq.getOctaves()); + + for (int i = 0; i < int(output.size()); ++i) { + checkCQFreqColumn(i, output[i], freq, interp); + } +} + +void +testCQFrequency(double freq) +{ + vector<CQSpectrogram::Interpolation> interpolationTypes; + interpolationTypes.push_back(CQSpectrogram::InterpolateZeros); + interpolationTypes.push_back(CQSpectrogram::InterpolateHold); + interpolationTypes.push_back(CQSpectrogram::InterpolateLinear); + + for (int k = 0; k < int(interpolationTypes.size()); ++k) { + CQSpectrogram::Interpolation interp = interpolationTypes[k]; + CQParameters params(sampleRate, cqmin, cqmax, bpo); + testCQFrequencyWith(params, interp, freq); + } +} + +BOOST_AUTO_TEST_CASE(freq_11) { testCQFrequency(11); } +BOOST_AUTO_TEST_CASE(freq_17) { testCQFrequency(17); } +BOOST_AUTO_TEST_CASE(freq_24) { testCQFrequency(24); } +BOOST_AUTO_TEST_CASE(freq_27) { testCQFrequency(27); } +BOOST_AUTO_TEST_CASE(freq_33) { testCQFrequency(33); } +BOOST_AUTO_TEST_CASE(freq_40) { testCQFrequency(40); } + +BOOST_AUTO_TEST_SUITE_END() +