annotate constant-q-cpp/test/TestCQFrequency.cpp @ 372:af71cbdab621 tip

Update bqvec code
author Chris Cannam
date Tue, 19 Nov 2019 10:13:32 +0000
parents 5d0a2ebb4d17
children
rev   line source
Chris@366 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@366 2
Chris@366 3 #include "cq/CQSpectrogram.h"
Chris@366 4
Chris@366 5 #include "dsp/Window.h"
Chris@366 6
Chris@366 7 #include <cmath>
Chris@366 8 #include <vector>
Chris@366 9 #include <iostream>
Chris@366 10
Chris@366 11 using std::vector;
Chris@366 12 using std::cerr;
Chris@366 13 using std::endl;
Chris@366 14
Chris@366 15 #define BOOST_TEST_DYN_LINK
Chris@366 16 #define BOOST_TEST_MAIN
Chris@366 17
Chris@366 18 #include <boost/test/unit_test.hpp>
Chris@366 19
Chris@366 20 BOOST_AUTO_TEST_SUITE(TestCQFrequency)
Chris@366 21
Chris@366 22 // The principle here is to feed a single windowed sinusoid into a
Chris@366 23 // small CQ transform and check that the output has its peak bin at
Chris@366 24 // the correct frequency.
Chris@366 25
Chris@366 26 // Set up fs/2 = 50, frequency range 10 -> 40 i.e. 2 octaves, fixed
Chris@366 27 // duration of 2 seconds
Chris@366 28 static const double sampleRate = 100;
Chris@366 29 static const double cqmin = 11.8921;
Chris@366 30 static const double cqmax = 40;
Chris@366 31 static const double bpo = 4;
Chris@366 32 static const int duration = sampleRate * 2;
Chris@366 33
Chris@366 34 // Threshold below which to ignore a column completely
Chris@366 35 static const double threshold = 0.08;
Chris@366 36
Chris@366 37 int
Chris@366 38 binForFrequency(double freq)
Chris@366 39 {
Chris@366 40 int bin = (bpo * 2) - round(bpo * log2(freq / cqmin)) - 1;
Chris@366 41 return bin;
Chris@366 42 }
Chris@366 43
Chris@366 44 void
Chris@366 45 checkCQFreqColumn(int i, vector<double> column,
Chris@366 46 double freq, CQSpectrogram::Interpolation interp)
Chris@366 47 {
Chris@366 48 double maxval = 0.0;
Chris@366 49 int maxidx = -1;
Chris@366 50 int height = column.size();
Chris@366 51
Chris@366 52 int nonZeroHeight = ((i % 2 == 1) ? height/2 : height);
Chris@366 53
Chris@366 54 for (int j = 0; j < nonZeroHeight; ++j) {
Chris@366 55 if (j == 0 || column[j] > maxval) {
Chris@366 56 maxval = column[j];
Chris@366 57 maxidx = j;
Chris@366 58 }
Chris@366 59 }
Chris@366 60
Chris@366 61 int expected = binForFrequency(freq);
Chris@366 62 if (maxval < threshold) {
Chris@366 63 return; // ignore these columns at start and end
Chris@366 64 } else if (expected < nonZeroHeight && maxidx != expected) {
Chris@366 65 cerr << "ERROR: In column " << i << " with interpolation " << interp
Chris@366 66 << ", maximum value for frequency " << freq
Chris@366 67 << "\n found at index " << maxidx
Chris@366 68 << " (expected index " << expected << ")" << endl;
Chris@366 69 cerr << "column contains: ";
Chris@366 70 for (int j = 0; j < height; ++j) {
Chris@366 71 cerr << column[j] << " ";
Chris@366 72 }
Chris@366 73 cerr << endl;
Chris@366 74 BOOST_CHECK_EQUAL(maxidx, expected);
Chris@366 75 }
Chris@366 76 }
Chris@366 77
Chris@366 78 void
Chris@366 79 testCQFrequencyWith(CQParameters params,
Chris@366 80 CQSpectrogram::Interpolation interp,
Chris@366 81 double freq)
Chris@366 82 {
Chris@366 83 CQSpectrogram cq(params, interp);
Chris@366 84
Chris@366 85 BOOST_CHECK_EQUAL(cq.getBinsPerOctave(), bpo);
Chris@366 86 BOOST_CHECK_EQUAL(cq.getOctaves(), 2);
Chris@366 87 BOOST_CHECK_CLOSE(cq.getBinFrequency(0), 40, 1e-10);
Chris@366 88 BOOST_CHECK_CLOSE(cq.getBinFrequency(4), 20, 1e-10);
Chris@366 89 BOOST_CHECK_CLOSE(cq.getBinFrequency(7), cqmin, 1e-3);
Chris@366 90
Chris@366 91 vector<double> input;
Chris@366 92 for (int i = 0; i < duration; ++i) {
Chris@366 93 input.push_back(sin((i * 2 * M_PI * freq) / sampleRate));
Chris@366 94 }
Chris@366 95 Window<double>(HanningWindow, duration).cut(input.data());
Chris@366 96
Chris@366 97 CQSpectrogram::RealBlock output = cq.process(input);
Chris@366 98 CQSpectrogram::RealBlock rest = cq.getRemainingOutput();
Chris@366 99 output.insert(output.end(), rest.begin(), rest.end());
Chris@366 100
Chris@366 101 BOOST_CHECK_EQUAL(output[0].size(),
Chris@366 102 cq.getBinsPerOctave() * cq.getOctaves());
Chris@366 103
Chris@366 104 for (int i = 0; i < int(output.size()); ++i) {
Chris@366 105 checkCQFreqColumn(i, output[i], freq, interp);
Chris@366 106 }
Chris@366 107 }
Chris@366 108
Chris@366 109 void
Chris@366 110 testCQFrequency(double freq)
Chris@366 111 {
Chris@366 112 vector<CQSpectrogram::Interpolation> interpolationTypes;
Chris@366 113 interpolationTypes.push_back(CQSpectrogram::InterpolateZeros);
Chris@366 114 interpolationTypes.push_back(CQSpectrogram::InterpolateHold);
Chris@366 115 interpolationTypes.push_back(CQSpectrogram::InterpolateLinear);
Chris@366 116
Chris@366 117 for (int k = 0; k < int(interpolationTypes.size()); ++k) {
Chris@366 118 CQSpectrogram::Interpolation interp = interpolationTypes[k];
Chris@366 119 CQParameters params(sampleRate, cqmin, cqmax, bpo);
Chris@366 120 testCQFrequencyWith(params, interp, freq);
Chris@366 121 }
Chris@366 122 }
Chris@366 123
Chris@366 124 BOOST_AUTO_TEST_CASE(freq_11) { testCQFrequency(11); }
Chris@366 125 BOOST_AUTO_TEST_CASE(freq_17) { testCQFrequency(17); }
Chris@366 126 BOOST_AUTO_TEST_CASE(freq_24) { testCQFrequency(24); }
Chris@366 127 BOOST_AUTO_TEST_CASE(freq_27) { testCQFrequency(27); }
Chris@366 128 BOOST_AUTO_TEST_CASE(freq_33) { testCQFrequency(33); }
Chris@366 129 BOOST_AUTO_TEST_CASE(freq_40) { testCQFrequency(40); }
Chris@366 130
Chris@366 131 BOOST_AUTO_TEST_SUITE_END()
Chris@366 132