annotate test/TestFeatureExtractor.cpp @ 128:3f32a88ee15a refactors

Comment
author Chris Cannam
date Thu, 11 Dec 2014 12:13:26 +0000
parents 2ed42b7616c5
children dad9fdc32a6a
rev   line source
Chris@126 1
Chris@126 2 #include "FeatureExtractor.h"
Chris@126 3
Chris@126 4 #include <vector>
Chris@126 5 #include <iostream>
Chris@126 6 #include <cmath>
Chris@126 7
Chris@126 8 using namespace std;
Chris@126 9
Chris@126 10 #define BOOST_TEST_DYN_LINK
Chris@126 11 #define BOOST_TEST_MAIN
Chris@126 12
Chris@126 13 #include <boost/test/unit_test.hpp>
Chris@126 14
Chris@126 15 static int freq2mid(double freq)
Chris@126 16 {
Chris@126 17 return round(57.0 + 12.0 * log(freq / 220.) / log(2.));
Chris@126 18 }
Chris@126 19
Chris@126 20 static int freq2chroma(double freq)
Chris@126 21 {
Chris@126 22 return freq2mid(freq) % 12;
Chris@126 23 }
Chris@126 24
Chris@127 25 static int bin2warped(int bin, int rate, int sz)
Chris@127 26 {
Chris@127 27 // see comments in nonchroma below
Chris@127 28 if (bin <= 33) return bin;
Chris@127 29 double freq = (double(bin) * rate) / sz;
Chris@127 30 int mid = freq2mid(freq);
Chris@127 31 if (mid > 127) mid = 127;
Chris@127 32 int outbin = mid - 77 + 33;
Chris@127 33 return outbin;
Chris@127 34 }
Chris@127 35
Chris@126 36 BOOST_AUTO_TEST_SUITE(TestFeatureExtractor)
Chris@126 37
Chris@126 38 BOOST_AUTO_TEST_CASE(chroma)
Chris@126 39 {
Chris@126 40 int rate = 44100;
Chris@126 41 int sz = 2048;
Chris@126 42 int hs = sz / 2 + 1;
Chris@126 43 int fsz = 13;
Chris@126 44
Chris@126 45 FeatureExtractor::Parameters params(rate, sz);
Chris@126 46 params.useChromaFrequencyMap = true;
Chris@126 47 FeatureExtractor fe(params);
Chris@126 48 BOOST_CHECK_EQUAL(fe.getFeatureSize(), fsz);
Chris@126 49
Chris@126 50 for (int bin = 0; bin < hs; ++bin) {
Chris@126 51
Chris@126 52 vector<double> real, imag;
Chris@126 53 real.resize(hs, 0.0);
Chris@126 54 imag.resize(hs, 0.0);
Chris@126 55
Chris@126 56 real[bin] += 10.0;
Chris@126 57 imag[bin] += 10.0;
Chris@126 58
Chris@126 59 // use two input sweeps, so we can test that they are properly
Chris@126 60 // summed into the output bin
Chris@126 61 real[hs-bin-1] += 5.0;
Chris@126 62 imag[hs-bin-1] += 5.0;
Chris@126 63
Chris@126 64 vector<double> out = fe.process(real, imag);
Chris@126 65
Chris@126 66 // We expect to find all bins are 0 except for:
Chris@126 67 //
Chris@126 68 // * two bins of 200 and 50 respectively, if the two input
Chris@126 69 // bins are distinct and their output chroma are also distinct
Chris@126 70 //
Chris@126 71 // * one bin of value 250 (= 10^2 + 5^2), if the two input
Chris@126 72 // bins are distinct but their output chroma are not
Chris@126 73 //
Chris@126 74 // * one bin of value 450 (= 15^2 + 15^2), if the input bins
Chris@128 75 // are not distinct (the feature extractor sums energies
Chris@128 76 // rather than magnitudes so as to integrate energy for a
Chris@128 77 // partial in the face of spectral leakage)
Chris@126 78 //
Chris@126 79 // The bin corresponding to each input frequency is that of
Chris@126 80 // its semitone value (with C=0), except that input bin
Chris@126 81 // frequencies less than 362Hz are shepherded into the
Chris@126 82 // separate bin 0 (see docs in FeatureExtractor.h)
Chris@126 83
Chris@126 84 vector<double> expected(fsz);
Chris@126 85
Chris@126 86 double infreq1 = (double(bin) * rate) / sz;
Chris@126 87
Chris@126 88 if (bin == hs-bin-1) {
Chris@126 89 expected[freq2chroma(infreq1) + 1] += 450;
Chris@126 90 } else {
Chris@126 91 if (infreq1 < 362) {
Chris@126 92 expected[0] += 200;
Chris@126 93 } else {
Chris@126 94 expected[freq2chroma(infreq1) + 1] += 200;
Chris@126 95 }
Chris@126 96 double infreq2 = (double(hs-bin-1) * rate) / sz;
Chris@126 97 if (infreq2 < 362) {
Chris@126 98 expected[0] += 50;
Chris@126 99 } else {
Chris@126 100 expected[freq2chroma(infreq2) + 1] += 50;
Chris@126 101 }
Chris@126 102 }
Chris@126 103
Chris@126 104 BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(),
Chris@126 105 expected.begin(), expected.end());
Chris@126 106 }
Chris@126 107 }
Chris@126 108
Chris@127 109 BOOST_AUTO_TEST_CASE(nonchroma)
Chris@127 110 {
Chris@127 111 int rate = 44100;
Chris@127 112 int sz = 2048;
Chris@127 113 int hs = sz / 2 + 1;
Chris@127 114 int fsz = 84;
Chris@127 115
Chris@127 116 FeatureExtractor::Parameters params(rate, sz);
Chris@127 117 FeatureExtractor fe(params);
Chris@127 118 BOOST_CHECK_EQUAL(fe.getFeatureSize(), fsz);
Chris@127 119
Chris@127 120 for (int bin = 0; bin < hs; ++bin) {
Chris@127 121
Chris@127 122 vector<double> real, imag;
Chris@127 123 real.resize(hs, 0.0);
Chris@127 124 imag.resize(hs, 0.0);
Chris@127 125
Chris@127 126 real[bin] += 10.0;
Chris@127 127 imag[bin] += 10.0;
Chris@127 128
Chris@127 129 // use two input sweeps, so we can test that they are properly
Chris@127 130 // summed into the output bin
Chris@127 131 real[hs-bin-1] += 5.0;
Chris@127 132 imag[hs-bin-1] += 5.0;
Chris@127 133
Chris@127 134 vector<double> out = fe.process(real, imag);
Chris@127 135
Chris@127 136 // We expect to find all bins are 0 except for:
Chris@127 137 //
Chris@127 138 // * two bins of 200 and 50 respectively, if the two input
Chris@127 139 // bins are distinct and their output bins are also distinct
Chris@127 140 //
Chris@127 141 // * one bin of value 250 (= 10^2 + 5^2), if the two input
Chris@127 142 // bins are distinct but their output bins are not
Chris@127 143 //
Chris@127 144 // * one bin of value 450 (= 15^2 + 15^2), if the input bins
Chris@128 145 // are not distinct (the feature extractor sums energies
Chris@128 146 // rather than magnitudes so as to integrate energy for a
Chris@128 147 // partial in the face of spectral leakage)
Chris@127 148 //
Chris@127 149 // The first 34 input bins (i.e. up to and including bin 33,
Chris@127 150 // 733Hz, MIDI pitch 77.something) are mapped linearly, those
Chris@127 151 // above that and up to MIDI pitch 127 (12544Hz) are mapped
Chris@127 152 // logarithmically, remaining bins are all mapped into the
Chris@127 153 // final output bin.
Chris@127 154 //
Chris@127 155 // So MIDI pitches up to and including 77 are mapped linearly
Chris@127 156 // by frequency into 34 bins; those from 78-126 inclusive are
Chris@127 157 // mapped linearly by MIDI pitch into the next 49 bins;
Chris@127 158 // everything above goes into the last bin, for 84 bins total.
Chris@127 159
Chris@127 160 vector<double> expected(fsz);
Chris@127 161
Chris@127 162 if (bin == hs-bin-1) {
Chris@127 163 expected[bin2warped(bin, rate, sz)] += 450;
Chris@127 164 } else {
Chris@127 165 expected[bin2warped(bin, rate, sz)] += 200;
Chris@127 166 expected[bin2warped(hs-bin-1, rate, sz)] += 50;
Chris@127 167 }
Chris@127 168
Chris@127 169 BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(),
Chris@127 170 expected.begin(), expected.end());
Chris@127 171 }
Chris@127 172 }
Chris@127 173
Chris@126 174 BOOST_AUTO_TEST_SUITE_END()