annotate test/TestFeatureExtractor.cpp @ 246:aac9ad4064ea subsequence tip

Fix incorrect handling of silent tail in the non-subsequence MATCH phase; some debug output changes
author Chris Cannam
date Fri, 24 Jul 2020 14:29:55 +0100
parents ccdadbdd80df
children
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@165 17 return int(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@185 38 void checkAlternateProcessType(FeatureExtractor &fe, feature_t expected,
Chris@130 39 vector<double> real, vector<double> imag)
Chris@130 40 {
Chris@130 41 vector<float> in;
Chris@188 42 for (size_t i = 0; i < real.size(); ++i) {
Chris@130 43 in.push_back(float(real[i]));
Chris@130 44 in.push_back(float(imag[i]));
Chris@130 45 }
Chris@185 46 feature_t alt = fe.process(&in[0]);
Chris@130 47 BOOST_CHECK_EQUAL_COLLECTIONS(alt.begin(), alt.end(),
Chris@130 48 expected.begin(), expected.end());
Chris@130 49 }
Chris@130 50
Chris@126 51 BOOST_AUTO_TEST_CASE(chroma)
Chris@126 52 {
Chris@129 53 int szs[] = { 1024, 2048, 4000 };
Chris@129 54 int rates[] = { 44100, 48000 };
Chris@126 55
Chris@129 56 for (int irate = 0; irate < int(sizeof(rates)/sizeof(rates[0])); ++irate) {
Chris@126 57
Chris@129 58 int rate = rates[irate];
Chris@126 59
Chris@129 60 for (int isz = 0; isz < int(sizeof(szs)/sizeof(szs[0])); ++isz) {
Chris@129 61
Chris@129 62 int sz = szs[isz];
Chris@129 63
Chris@129 64 int hs = sz / 2 + 1;
Chris@129 65 int fsz = 13;
Chris@129 66
Chris@216 67 FeatureExtractor::Parameters params { float(rate) };
Chris@216 68 params.fftSize = sz;
Chris@129 69 params.useChromaFrequencyMap = true;
Chris@196 70 params.minFrequency = 0;
Chris@129 71 FeatureExtractor fe(params);
Chris@129 72 BOOST_CHECK_EQUAL(fe.getFeatureSize(), fsz);
Chris@129 73
Chris@129 74 for (int bin = 0; bin < hs; ++bin) {
Chris@129 75
Chris@129 76 vector<double> real, imag;
Chris@129 77 real.resize(hs, 0.0);
Chris@129 78 imag.resize(hs, 0.0);
Chris@126 79
Chris@129 80 real[bin] += 10.0;
Chris@129 81 imag[bin] += 10.0;
Chris@126 82
Chris@129 83 // use two input sweeps, so we can test that they are
Chris@129 84 // properly summed into the output bin
Chris@129 85 real[hs-bin-1] += 5.0;
Chris@129 86 imag[hs-bin-1] += 5.0;
Chris@129 87
Chris@185 88 feature_t out = fe.process(real, imag);
Chris@126 89
Chris@130 90 checkAlternateProcessType(fe, out, real, imag);
Chris@130 91
Chris@129 92 // We expect to find all bins are 0 except for:
Chris@129 93 //
Chris@129 94 // * two bins of 200 and 50 respectively, if the two input
Chris@129 95 // bins are distinct and their output chroma are also distinct
Chris@129 96 //
Chris@129 97 // * one bin of value 250 (= 10^2 + 5^2), if the two input
Chris@129 98 // bins are distinct but their output chroma are not
Chris@129 99 //
Chris@129 100 // * one bin of value 450 (= 15^2 + 15^2), if the input bins
Chris@129 101 // are not distinct (the feature extractor sums energies
Chris@129 102 // rather than magnitudes so as to integrate energy for a
Chris@129 103 // partial in the face of spectral leakage)
Chris@129 104 //
Chris@129 105 // The bin corresponding to each input frequency is
Chris@129 106 // that of its semitone value (with C=0), except that
Chris@129 107 // input bins less than the 17th are shepherded into
Chris@129 108 // the separate bin 0 (see docs in FeatureExtractor.h)
Chris@126 109
Chris@129 110 double cutoff = (17.0 * rate) / sz;
Chris@129 111
Chris@185 112 feature_t expected(fsz);
Chris@126 113
Chris@129 114 double infreq1 = (double(bin) * rate) / sz;
Chris@126 115
Chris@129 116 if (bin == hs-bin-1) {
Chris@129 117 expected[freq2chroma(infreq1) + 1] += 450;
Chris@129 118 } else {
Chris@129 119 if (infreq1 < cutoff) {
Chris@129 120 expected[0] += 200;
Chris@129 121 } else {
Chris@129 122 expected[freq2chroma(infreq1) + 1] += 200;
Chris@129 123 }
Chris@129 124 double infreq2 = (double(hs-bin-1) * rate) / sz;
Chris@129 125 if (infreq2 < cutoff) {
Chris@129 126 expected[0] += 50;
Chris@129 127 } else {
Chris@129 128 expected[freq2chroma(infreq2) + 1] += 50;
Chris@129 129 }
Chris@129 130 }
Chris@126 131
Chris@129 132 BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(),
Chris@129 133 expected.begin(), expected.end());
Chris@126 134 }
Chris@126 135 }
Chris@126 136 }
Chris@126 137 }
Chris@126 138
Chris@127 139 BOOST_AUTO_TEST_CASE(nonchroma)
Chris@127 140 {
Chris@127 141 int rate = 44100;
Chris@127 142 int sz = 2048;
Chris@127 143 int hs = sz / 2 + 1;
Chris@127 144 int fsz = 84;
Chris@127 145
Chris@216 146 FeatureExtractor::Parameters params { float(rate) };
Chris@216 147 params.fftSize = sz;
Chris@196 148 params.minFrequency = 0;
Chris@127 149 FeatureExtractor fe(params);
Chris@127 150 BOOST_CHECK_EQUAL(fe.getFeatureSize(), fsz);
Chris@127 151
Chris@127 152 for (int bin = 0; bin < hs; ++bin) {
Chris@127 153
Chris@127 154 vector<double> real, imag;
Chris@127 155 real.resize(hs, 0.0);
Chris@127 156 imag.resize(hs, 0.0);
Chris@127 157
Chris@127 158 real[bin] += 10.0;
Chris@127 159 imag[bin] += 10.0;
Chris@127 160
Chris@127 161 // use two input sweeps, so we can test that they are properly
Chris@127 162 // summed into the output bin
Chris@127 163 real[hs-bin-1] += 5.0;
Chris@127 164 imag[hs-bin-1] += 5.0;
Chris@127 165
Chris@185 166 feature_t out = fe.process(real, imag);
Chris@127 167
Chris@130 168 checkAlternateProcessType(fe, out, real, imag);
Chris@130 169
Chris@127 170 // We expect to find all bins are 0 except for:
Chris@127 171 //
Chris@127 172 // * two bins of 200 and 50 respectively, if the two input
Chris@127 173 // bins are distinct and their output bins are also distinct
Chris@127 174 //
Chris@127 175 // * one bin of value 250 (= 10^2 + 5^2), if the two input
Chris@127 176 // bins are distinct but their output bins are not
Chris@127 177 //
Chris@127 178 // * one bin of value 450 (= 15^2 + 15^2), if the input bins
Chris@128 179 // are not distinct (the feature extractor sums energies
Chris@128 180 // rather than magnitudes so as to integrate energy for a
Chris@128 181 // partial in the face of spectral leakage)
Chris@127 182 //
Chris@127 183 // The first 34 input bins (i.e. up to and including bin 33,
Chris@127 184 // 733Hz, MIDI pitch 77.something) are mapped linearly, those
Chris@127 185 // above that and up to MIDI pitch 127 (12544Hz) are mapped
Chris@127 186 // logarithmically, remaining bins are all mapped into the
Chris@127 187 // final output bin.
Chris@127 188 //
Chris@127 189 // So MIDI pitches up to and including 77 are mapped linearly
Chris@127 190 // by frequency into 34 bins; those from 78-126 inclusive are
Chris@127 191 // mapped linearly by MIDI pitch into the next 49 bins;
Chris@127 192 // everything above goes into the last bin, for 84 bins total.
Chris@127 193
Chris@185 194 feature_t expected(fsz);
Chris@127 195
Chris@127 196 if (bin == hs-bin-1) {
Chris@127 197 expected[bin2warped(bin, rate, sz)] += 450;
Chris@127 198 } else {
Chris@127 199 expected[bin2warped(bin, rate, sz)] += 200;
Chris@127 200 expected[bin2warped(hs-bin-1, rate, sz)] += 50;
Chris@127 201 }
Chris@127 202
Chris@127 203 BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(),
Chris@127 204 expected.begin(), expected.end());
Chris@127 205 }
Chris@127 206 }
Chris@127 207
Chris@126 208 BOOST_AUTO_TEST_SUITE_END()