c@131
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
c@131
|
2
|
c@131
|
3 #include "cq/CQSpectrogram.h"
|
c@131
|
4
|
c@131
|
5 #include "dsp/Window.h"
|
c@131
|
6
|
c@131
|
7 #include <cmath>
|
c@131
|
8 #include <vector>
|
c@132
|
9 #include <iostream>
|
c@131
|
10
|
c@131
|
11 using std::vector;
|
c@132
|
12 using std::cerr;
|
c@132
|
13 using std::endl;
|
c@131
|
14
|
c@131
|
15 #define BOOST_TEST_DYN_LINK
|
c@131
|
16 #define BOOST_TEST_MAIN
|
c@131
|
17
|
c@131
|
18 #include <boost/test/unit_test.hpp>
|
c@131
|
19
|
c@131
|
20 BOOST_AUTO_TEST_SUITE(TestCQFrequency)
|
c@131
|
21
|
c@131
|
22 // The principle here is to feed a single windowed sinusoid into a
|
c@131
|
23 // small CQ transform and check that the output has its peak bin at
|
c@131
|
24 // the correct frequency. We can repeat for different frequencies both
|
c@131
|
25 // inside and outside the frequency range supported by the CQ. We
|
c@131
|
26 // should also repeat for CQSpectrogram outputs as well as the raw CQ.
|
c@131
|
27
|
c@131
|
28 // Set up fs/2 = 50, frequency range 10 -> 40 i.e. 2 octaves, fixed
|
c@131
|
29 // duration of 2 seconds
|
c@131
|
30 static const double sampleRate = 100;
|
c@131
|
31 static const double cqmin = 10;
|
c@131
|
32 static const double cqmax = 40;
|
c@131
|
33 static const double bpo = 4;
|
c@131
|
34 static const int duration = sampleRate * 2;
|
c@131
|
35
|
c@132
|
36 // Threshold below which to ignore a column completely
|
c@131
|
37 static const double threshold = 0.08;
|
c@131
|
38
|
c@132
|
39 int
|
c@132
|
40 binForFrequency(double freq)
|
c@132
|
41 {
|
c@132
|
42 int bin = (2 * bpo) - round(bpo * log2(freq / cqmin));
|
c@132
|
43 cerr << "binForFrequency: " << freq << " -> " << bin << endl;
|
c@132
|
44 return bin;
|
c@132
|
45 }
|
c@132
|
46
|
c@131
|
47 void
|
c@132
|
48 checkCQFreqColumn(int i, vector<double> column, double freq)
|
c@131
|
49 {
|
c@132
|
50 double maxval = 0.0;
|
c@132
|
51 int maxidx = -1;
|
c@132
|
52 int height = column.size();
|
c@132
|
53 for (int j = 0; j < height; ++j) {
|
c@132
|
54 if (j == 0 || column[j] > maxval) {
|
c@132
|
55 maxval = column[j];
|
c@132
|
56 maxidx = j;
|
c@132
|
57 }
|
c@132
|
58 }
|
c@132
|
59 cerr << "maxval = " << maxval << " at " << maxidx << endl;
|
c@132
|
60 int expected = binForFrequency(freq);
|
c@132
|
61 if (maxval < threshold) {
|
c@132
|
62 return; // ignore these columns at start and end
|
c@132
|
63 } else if (expected < 0 || expected >= height) {
|
c@132
|
64 cerr << "maxval = " << maxval << endl;
|
c@132
|
65 BOOST_CHECK(maxval < threshold);
|
c@132
|
66 } else {
|
c@132
|
67 BOOST_CHECK_EQUAL(maxidx, expected);
|
c@132
|
68 }
|
c@131
|
69 }
|
c@131
|
70
|
c@131
|
71 void
|
c@131
|
72 testCQFrequency(double freq)
|
c@131
|
73 {
|
c@131
|
74 CQParameters params(sampleRate, cqmin, cqmax, bpo);
|
c@131
|
75 CQSpectrogram cq(params, CQSpectrogram::InterpolateLinear);
|
c@131
|
76
|
c@132
|
77 BOOST_CHECK_EQUAL(cq.getBinsPerOctave(), bpo);
|
c@132
|
78 BOOST_CHECK_EQUAL(cq.getOctaves(), 2);
|
c@132
|
79
|
c@131
|
80 vector<double> input;
|
c@131
|
81 for (int i = 0; i < duration; ++i) {
|
c@131
|
82 input.push_back(sin((i * 2 * M_PI * freq) / sampleRate));
|
c@131
|
83 }
|
c@131
|
84 Window<double>(HanningWindow, duration).cut(input.data());
|
c@131
|
85
|
c@131
|
86 CQSpectrogram::RealBlock output = cq.process(input);
|
c@131
|
87 CQSpectrogram::RealBlock rest = cq.getRemainingOutput();
|
c@131
|
88 output.insert(output.end(), rest.begin(), rest.end());
|
c@131
|
89
|
c@132
|
90 BOOST_CHECK_EQUAL(output[0].size(), cq.getBinsPerOctave() * cq.getOctaves());
|
c@132
|
91
|
c@132
|
92 for (int i = 0; i < int(output.size()); ++i) {
|
c@132
|
93 checkCQFreqColumn(i, output[i], freq);
|
c@132
|
94 }
|
c@131
|
95 }
|
c@131
|
96
|
c@131
|
97 BOOST_AUTO_TEST_CASE(freq_5) { testCQFrequency(5); }
|
c@131
|
98 BOOST_AUTO_TEST_CASE(freq_10) { testCQFrequency(10); }
|
c@131
|
99 BOOST_AUTO_TEST_CASE(freq_15) { testCQFrequency(15); }
|
c@131
|
100 BOOST_AUTO_TEST_CASE(freq_20) { testCQFrequency(20); }
|
c@131
|
101 BOOST_AUTO_TEST_CASE(freq_25) { testCQFrequency(25); }
|
c@131
|
102 BOOST_AUTO_TEST_CASE(freq_30) { testCQFrequency(30); }
|
c@131
|
103 BOOST_AUTO_TEST_CASE(freq_35) { testCQFrequency(35); }
|
c@131
|
104 BOOST_AUTO_TEST_CASE(freq_40) { testCQFrequency(40); }
|
c@131
|
105 BOOST_AUTO_TEST_CASE(freq_45) { testCQFrequency(45); }
|
c@131
|
106 BOOST_AUTO_TEST_CASE(freq_50) { testCQFrequency(50); }
|
c@131
|
107
|
c@131
|
108 BOOST_AUTO_TEST_SUITE_END()
|
c@131
|
109
|