c@135: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ c@135: c@135: #include "cq/CQSpectrogram.h" c@135: c@135: #include "dsp/Window.h" c@135: c@135: #include c@135: #include c@135: #include c@135: c@135: using std::vector; c@135: using std::cerr; c@135: using std::endl; c@135: c@135: #define BOOST_TEST_DYN_LINK c@135: #define BOOST_TEST_MAIN c@135: c@135: #include c@135: c@135: BOOST_AUTO_TEST_SUITE(TestCQTime) c@135: c@135: // Principle: Run a Dirac impulse through the CQ transform and check c@135: // that its output has all the peak bins aligned correctly in time. c@135: c@135: // Set up fs/2 = 50, frequency range 10 -> 40 i.e. 2 octaves, fixed c@135: // duration of 2 seconds c@135: static const double sampleRate = 100; c@135: static const double cqmin = 10; c@135: static const double cqmax = 40; c@135: static const double bpo = 4; c@135: static const int duration = sampleRate * 2; c@135: c@135: // Threshold below which to ignore a column completely c@135: static const double threshold = 0.08; c@135: c@135: void c@135: testCQTime(double t) c@135: { c@135: vector interpolationTypes; c@135: interpolationTypes.push_back(CQSpectrogram::InterpolateZeros); c@135: interpolationTypes.push_back(CQSpectrogram::InterpolateHold); c@135: interpolationTypes.push_back(CQSpectrogram::InterpolateLinear); c@135: c@135: for (int k = 0; k < int(interpolationTypes.size()); ++k) { c@135: c@139: CQSpectrogram::Interpolation interp = interpolationTypes[k]; c@139: c@135: CQParameters params(sampleRate, cqmin, cqmax, bpo); c@139: CQSpectrogram cq(params, interp); c@135: c@135: BOOST_CHECK_EQUAL(cq.getBinsPerOctave(), bpo); c@135: BOOST_CHECK_EQUAL(cq.getOctaves(), 2); c@135: c@139: vector input(duration, 0.0); c@139: int ix = int(floor(t * sampleRate)); c@139: if (ix >= duration) ix = duration-1; c@139: input[ix] = 1.0; c@135: c@135: CQSpectrogram::RealBlock output = cq.process(input); c@135: CQSpectrogram::RealBlock rest = cq.getRemainingOutput(); c@135: output.insert(output.end(), rest.begin(), rest.end()); c@135: c@135: BOOST_CHECK_EQUAL(output[0].size(), c@135: cq.getBinsPerOctave() * cq.getOctaves()); c@135: c@141: vector peaks; c@141: double eps = 1e-8; c@141: c@139: for (int j = 0; j < int(output[0].size()); ++j) { c@139: c@139: int maxidx = -1; c@139: double max = 0.0; c@139: for (int i = 0; i < int(output.size()); ++i) { c@139: double value = output[i][j]; c@141: if (i == 0 || value + eps > max) { c@139: max = value; c@139: maxidx = i; c@139: } c@139: } c@139: c@141: peaks.push_back(maxidx); c@141: } c@139: c@141: for (int j = 1; j < int(peaks.size()); ++j) { c@141: int oct = j / bpo; c@141: int spacing = (1 << oct); c@141: int actual = peaks[j]/spacing; c@141: int expected = int(round(double(peaks[0])/spacing)); c@141: if (actual != expected) { c@139: cerr << "ERROR: In row " << j << " (bin freq " c@139: << cq.getBinFrequency(j) << "), interpolation " << interp c@139: << ", maximum value for time " << t c@141: << "\n found at index " << peaks[j] c@141: << " of " << output.size() << " which does not align with" c@141: << " highest frequency\n bin peak at " << peaks[0] c@141: << " given octave spacing of " << spacing c@141: << "\n [latency = " << cq.getLatency() c@139: << ", hop = " << cq.getColumnHop() << ", duration = " c@141: << duration << ", ix = " << ix << "]" << endl; c@139: cerr << "row contains: "; c@139: for (int i = 0; i < int(output.size()); ++i) { c@141: if (i == expected * spacing) cerr << "*"; c@141: if (i == peaks[j]) cerr << "**"; c@139: cerr << output[i][j] << " "; c@139: } c@139: cerr << endl; c@139: c@141: BOOST_CHECK_EQUAL(actual, expected); c@139: } c@139: } c@135: } c@135: } c@135: c@135: BOOST_AUTO_TEST_CASE(time_zero) { testCQTime(0); } c@135: BOOST_AUTO_TEST_CASE(time_half) { testCQTime(0.5); } c@135: BOOST_AUTO_TEST_CASE(time_one) { testCQTime(1.0); } c@135: BOOST_AUTO_TEST_CASE(time_two) { testCQTime(2.0); } c@135: c@135: BOOST_AUTO_TEST_SUITE_END() c@135: