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