Chris@126: Chris@126: #include "FeatureExtractor.h" Chris@126: Chris@126: #include Chris@126: #include Chris@126: #include Chris@126: Chris@126: using namespace std; Chris@126: Chris@126: #define BOOST_TEST_DYN_LINK Chris@126: #define BOOST_TEST_MAIN Chris@126: Chris@126: #include Chris@126: Chris@126: static int freq2mid(double freq) Chris@126: { Chris@126: return round(57.0 + 12.0 * log(freq / 220.) / log(2.)); Chris@126: } Chris@126: Chris@126: static int freq2chroma(double freq) Chris@126: { Chris@126: return freq2mid(freq) % 12; Chris@126: } Chris@126: Chris@127: static int bin2warped(int bin, int rate, int sz) Chris@127: { Chris@127: // see comments in nonchroma below Chris@127: if (bin <= 33) return bin; Chris@127: double freq = (double(bin) * rate) / sz; Chris@127: int mid = freq2mid(freq); Chris@127: if (mid > 127) mid = 127; Chris@127: int outbin = mid - 77 + 33; Chris@127: return outbin; Chris@127: } Chris@127: Chris@126: BOOST_AUTO_TEST_SUITE(TestFeatureExtractor) Chris@126: Chris@126: BOOST_AUTO_TEST_CASE(chroma) Chris@126: { Chris@126: int rate = 44100; Chris@126: int sz = 2048; Chris@126: int hs = sz / 2 + 1; Chris@126: int fsz = 13; Chris@126: Chris@126: FeatureExtractor::Parameters params(rate, sz); Chris@126: params.useChromaFrequencyMap = true; Chris@126: FeatureExtractor fe(params); Chris@126: BOOST_CHECK_EQUAL(fe.getFeatureSize(), fsz); Chris@126: Chris@126: for (int bin = 0; bin < hs; ++bin) { Chris@126: Chris@126: vector real, imag; Chris@126: real.resize(hs, 0.0); Chris@126: imag.resize(hs, 0.0); Chris@126: Chris@126: real[bin] += 10.0; Chris@126: imag[bin] += 10.0; Chris@126: Chris@126: // use two input sweeps, so we can test that they are properly Chris@126: // summed into the output bin Chris@126: real[hs-bin-1] += 5.0; Chris@126: imag[hs-bin-1] += 5.0; Chris@126: Chris@126: vector out = fe.process(real, imag); Chris@126: Chris@126: // We expect to find all bins are 0 except for: Chris@126: // Chris@126: // * two bins of 200 and 50 respectively, if the two input Chris@126: // bins are distinct and their output chroma are also distinct Chris@126: // Chris@126: // * one bin of value 250 (= 10^2 + 5^2), if the two input Chris@126: // bins are distinct but their output chroma are not Chris@126: // Chris@126: // * one bin of value 450 (= 15^2 + 15^2), if the input bins Chris@126: // are not distinct Chris@126: // Chris@126: // The bin corresponding to each input frequency is that of Chris@126: // its semitone value (with C=0), except that input bin Chris@126: // frequencies less than 362Hz are shepherded into the Chris@126: // separate bin 0 (see docs in FeatureExtractor.h) Chris@126: Chris@126: vector expected(fsz); Chris@126: Chris@126: double infreq1 = (double(bin) * rate) / sz; Chris@126: Chris@126: if (bin == hs-bin-1) { Chris@126: expected[freq2chroma(infreq1) + 1] += 450; Chris@126: } else { Chris@126: if (infreq1 < 362) { Chris@126: expected[0] += 200; Chris@126: } else { Chris@126: expected[freq2chroma(infreq1) + 1] += 200; Chris@126: } Chris@126: double infreq2 = (double(hs-bin-1) * rate) / sz; Chris@126: if (infreq2 < 362) { Chris@126: expected[0] += 50; Chris@126: } else { Chris@126: expected[freq2chroma(infreq2) + 1] += 50; Chris@126: } Chris@126: } Chris@126: Chris@126: BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), Chris@126: expected.begin(), expected.end()); Chris@126: } Chris@126: } Chris@126: Chris@127: BOOST_AUTO_TEST_CASE(nonchroma) Chris@127: { Chris@127: int rate = 44100; Chris@127: int sz = 2048; Chris@127: int hs = sz / 2 + 1; Chris@127: int fsz = 84; Chris@127: Chris@127: FeatureExtractor::Parameters params(rate, sz); Chris@127: FeatureExtractor fe(params); Chris@127: BOOST_CHECK_EQUAL(fe.getFeatureSize(), fsz); Chris@127: Chris@127: for (int bin = 0; bin < hs; ++bin) { Chris@127: Chris@127: vector real, imag; Chris@127: real.resize(hs, 0.0); Chris@127: imag.resize(hs, 0.0); Chris@127: Chris@127: real[bin] += 10.0; Chris@127: imag[bin] += 10.0; Chris@127: Chris@127: // use two input sweeps, so we can test that they are properly Chris@127: // summed into the output bin Chris@127: real[hs-bin-1] += 5.0; Chris@127: imag[hs-bin-1] += 5.0; Chris@127: Chris@127: vector out = fe.process(real, imag); Chris@127: Chris@127: // We expect to find all bins are 0 except for: Chris@127: // Chris@127: // * two bins of 200 and 50 respectively, if the two input Chris@127: // bins are distinct and their output bins are also distinct Chris@127: // Chris@127: // * one bin of value 250 (= 10^2 + 5^2), if the two input Chris@127: // bins are distinct but their output bins are not Chris@127: // Chris@127: // * one bin of value 450 (= 15^2 + 15^2), if the input bins Chris@127: // are not distinct Chris@127: // Chris@127: // The first 34 input bins (i.e. up to and including bin 33, Chris@127: // 733Hz, MIDI pitch 77.something) are mapped linearly, those Chris@127: // above that and up to MIDI pitch 127 (12544Hz) are mapped Chris@127: // logarithmically, remaining bins are all mapped into the Chris@127: // final output bin. Chris@127: // Chris@127: // So MIDI pitches up to and including 77 are mapped linearly Chris@127: // by frequency into 34 bins; those from 78-126 inclusive are Chris@127: // mapped linearly by MIDI pitch into the next 49 bins; Chris@127: // everything above goes into the last bin, for 84 bins total. Chris@127: Chris@127: vector expected(fsz); Chris@127: Chris@127: if (bin == hs-bin-1) { Chris@127: expected[bin2warped(bin, rate, sz)] += 450; Chris@127: } else { Chris@127: expected[bin2warped(bin, rate, sz)] += 200; Chris@127: expected[bin2warped(hs-bin-1, rate, sz)] += 50; Chris@127: } Chris@127: Chris@127: BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), Chris@127: expected.begin(), expected.end()); Chris@127: } Chris@127: } Chris@127: Chris@126: BOOST_AUTO_TEST_SUITE_END()