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.

Statistics Download as Zip
| Branch: | Revision:

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