annotate tests/TestPhaseVocoder.cpp @ 209:ccd2019190bf msvc

Some MSVC fixes, including (temporarily, probably) renaming the FFT source file to avoid getting it mixed up with the Vamp SDK one in our object dir
author Chris Cannam
date Thu, 01 Feb 2018 16:34:08 +0000
parents 6ec45e85ed81
children 2de6184b2ce0
rev   line source
Chris@117 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@112 2
Chris@112 3 #include "dsp/phasevocoder/PhaseVocoder.h"
Chris@112 4
Chris@112 5 #include "base/Window.h"
Chris@112 6
Chris@117 7 #include <iostream>
Chris@117 8
Chris@117 9 using std::cerr;
Chris@117 10 using std::endl;
Chris@117 11
Chris@112 12 #define BOOST_TEST_DYN_LINK
Chris@112 13 #define BOOST_TEST_MAIN
Chris@112 14
Chris@112 15 #include <boost/test/unit_test.hpp>
Chris@112 16
Chris@112 17 BOOST_AUTO_TEST_SUITE(TestFFT)
Chris@112 18
Chris@112 19 #define COMPARE_CONST(a, n) \
Chris@112 20 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
Chris@117 21 BOOST_CHECK_SMALL(a[cmp_i] - n, 1e-7); \
Chris@112 22 }
Chris@112 23
Chris@112 24 #define COMPARE_ARRAY(a, b) \
Chris@112 25 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
Chris@117 26 BOOST_CHECK_SMALL(a[cmp_i] - b[cmp_i], 1e-7); \
Chris@112 27 }
Chris@112 28
Chris@112 29 #define COMPARE_ARRAY_EXACT(a, b) \
Chris@112 30 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
Chris@112 31 BOOST_CHECK_EQUAL(a[cmp_i], b[cmp_i]); \
Chris@112 32 }
Chris@112 33
Chris@112 34 BOOST_AUTO_TEST_CASE(fullcycle)
Chris@112 35 {
Chris@115 36 // Cosine with one cycle exactly equal to pvoc hopsize. This is
Chris@115 37 // pretty much the most trivial case -- in fact it's
Chris@115 38 // indistinguishable from totally silent input (in the phase
Chris@115 39 // values) because the measured phases are zero throughout.
Chris@115 40
Chris@115 41 // We aren't windowing the input frame because (for once) it
Chris@115 42 // actually *is* just a short part of a continuous infinite
Chris@115 43 // sinusoid.
Chris@112 44
Chris@112 45 double frame[] = { 1, 0, -1, 0, 1, 0, -1, 0 };
Chris@112 46
Chris@115 47 PhaseVocoder pvoc(8, 4);
Chris@112 48
Chris@112 49 // Make these arrays one element too long at each end, so as to
Chris@112 50 // test for overruns. For frame size 8, we expect 8/2+1 = 5
Chris@112 51 // mag/phase pairs.
Chris@112 52 double mag[] = { 999, 999, 999, 999, 999, 999, 999 };
Chris@112 53 double phase[] = { 999, 999, 999, 999, 999, 999, 999 };
Chris@115 54 double unw[] = { 999, 999, 999, 999, 999, 999, 999 };
Chris@112 55
Chris@119 56 pvoc.processTimeDomain(frame, mag + 1, phase + 1, unw + 1);
Chris@112 57
Chris@112 58 double magExpected0[] = { 999, 0, 0, 4, 0, 0, 999 };
Chris@112 59 COMPARE_ARRAY_EXACT(mag, magExpected0);
Chris@112 60
Chris@112 61 double phaseExpected0[] = { 999, 0, 0, 0, 0, 0, 999 };
Chris@119 62 COMPARE_ARRAY(phase, phaseExpected0);
Chris@112 63
Chris@115 64 double unwExpected0[] = { 999, 0, 0, 0, 0, 0, 999 };
Chris@115 65 COMPARE_ARRAY(unw, unwExpected0);
Chris@115 66
Chris@119 67 pvoc.processTimeDomain(frame, mag + 1, phase + 1, unw + 1);
Chris@112 68
Chris@112 69 double magExpected1[] = { 999, 0, 0, 4, 0, 0, 999 };
Chris@112 70 COMPARE_ARRAY_EXACT(mag, magExpected1);
Chris@112 71
Chris@115 72 double phaseExpected1[] = { 999, 0, 0, 0, 0, 0, 999 };
Chris@112 73 COMPARE_ARRAY(phase, phaseExpected1);
Chris@113 74
Chris@117 75 // Derivation of unwrapped values:
Chris@115 76 //
Chris@115 77 // * Bin 0 (DC) always has phase 0 and expected phase 0
Chris@115 78 //
Chris@115 79 // * Bin 1 has expected phase pi (the hop size is half a cycle at
Chris@115 80 // its frequency), but measured phase 0 (because there is no
Chris@115 81 // signal in that bin). So it has phase error -pi, which is
Chris@115 82 // mapped into (-pi,pi] range as pi, giving an unwrapped phase
Chris@115 83 // of 2*pi.
Chris@115 84 //
Chris@119 85 // * Bin 2 has expected phase 2*pi, measured phase 0, hence error
Chris@119 86 // 0 and unwrapped phase 2*pi.
Chris@115 87 //
Chris@115 88 // * Bin 3 is like bin 1: it has expected phase 3*pi, measured
Chris@115 89 // phase 0, so phase error -pi and unwrapped phase 4*pi.
Chris@115 90 //
Chris@119 91 // * Bin 4 (Nyquist) has expected phase 4*pi, measured phase 0,
Chris@119 92 // hence error 0 and unwrapped phase 4*pi.
Chris@115 93
Chris@115 94 double unwExpected1[] = { 999, 0, 2*M_PI, 2*M_PI, 4*M_PI, 4*M_PI, 999 };
Chris@115 95 COMPARE_ARRAY(unw, unwExpected1);
Chris@115 96
Chris@119 97 pvoc.processTimeDomain(frame, mag + 1, phase + 1, unw + 1);
Chris@113 98
Chris@113 99 double magExpected2[] = { 999, 0, 0, 4, 0, 0, 999 };
Chris@113 100 COMPARE_ARRAY_EXACT(mag, magExpected2);
Chris@113 101
Chris@115 102 double phaseExpected2[] = { 999, 0, 0, 0, 0, 0, 999 };
Chris@113 103 COMPARE_ARRAY(phase, phaseExpected2);
Chris@115 104
Chris@115 105 double unwExpected2[] = { 999, 0, 4*M_PI, 4*M_PI, 8*M_PI, 8*M_PI, 999 };
Chris@115 106 COMPARE_ARRAY(unw, unwExpected2);
Chris@112 107 }
Chris@112 108
Chris@117 109 BOOST_AUTO_TEST_CASE(overlapping)
Chris@117 110 {
Chris@117 111 // Sine (i.e. cosine starting at phase -pi/2) starting with the
Chris@117 112 // first sample, introducing a cosine of half the frequency
Chris@117 113 // starting at the fourth sample, i.e. the second hop. The cosine
Chris@117 114 // is introduced "by magic", i.e. it doesn't appear in the second
Chris@117 115 // half of the first frame (it would have quite strange effects on
Chris@117 116 // the first frame if it did).
Chris@115 117
Chris@117 118 double data[32] = { // 3 x 8-sample frames which we pretend are overlapping
Chris@117 119 0, 1, 0, -1, 0, 1, 0, -1,
Chris@117 120 1, 1.70710678, 0, -1.70710678, -1, 0.29289322, 0, -0.29289322,
Chris@117 121 -1, 0.29289322, 0, -0.29289322, 1, 1.70710678, 0, -1.70710678,
Chris@117 122 };
Chris@117 123
Chris@117 124 PhaseVocoder pvoc(8, 4);
Chris@117 125
Chris@117 126 // Make these arrays one element too long at each end, so as to
Chris@117 127 // test for overruns. For frame size 8, we expect 8/2+1 = 5
Chris@117 128 // mag/phase pairs.
Chris@117 129 double mag[] = { 999, 999, 999, 999, 999, 999, 999 };
Chris@117 130 double phase[] = { 999, 999, 999, 999, 999, 999, 999 };
Chris@117 131 double unw[] = { 999, 999, 999, 999, 999, 999, 999 };
Chris@117 132
Chris@119 133 pvoc.processTimeDomain(data, mag + 1, phase + 1, unw + 1);
Chris@117 134
Chris@117 135 double magExpected0[] = { 999, 0, 0, 4, 0, 0, 999 };
Chris@117 136 COMPARE_ARRAY(mag, magExpected0);
Chris@117 137
Chris@117 138 double phaseExpected0[] = { 999, 0, 0, -M_PI/2 , 0, 0, 999 };
Chris@117 139 COMPARE_ARRAY(phase, phaseExpected0);
Chris@117 140
Chris@117 141 double unwExpected0[] = { 999, 0, 0, -M_PI/2, 0, 0, 999 };
Chris@117 142 COMPARE_ARRAY(unw, unwExpected0);
Chris@117 143
Chris@119 144 pvoc.processTimeDomain(data + 8, mag + 1, phase + 1, unw + 1);
Chris@117 145
Chris@117 146 double magExpected1[] = { 999, 0, 4, 4, 0, 0, 999 };
Chris@117 147 COMPARE_ARRAY(mag, magExpected1);
Chris@117 148
Chris@119 149 // Derivation of unwrapped values:
Chris@119 150 //
Chris@119 151 // * Bin 0 (DC) always has phase 0 and expected phase 0
Chris@119 152 //
Chris@119 153 // * Bin 1 has a new signal, a cosine starting with phase 0. But
Chris@119 154 // because of the "FFT shift" which the phase vocoder carries
Chris@119 155 // out to place zero phase in the centre of the (usually
Chris@119 156 // windowed) frame, and because a single cycle at this frequency
Chris@119 157 // spans the whole frame, this bin actually has measured phase
Chris@119 158 // of either pi or -pi. (The shift doesn't affect those
Chris@119 159 // higher-frequency bins whose signals fit exact multiples of a
Chris@119 160 // cycle into a frame.) This maps into (-pi,pi] as pi, which
Chris@119 161 // matches the expected phase, hence unwrapped phase is also pi.
Chris@119 162 //
Chris@119 163 // * Bin 2 has expected phase 3pi/2 (being the previous measured
Chris@119 164 // phase of -pi/2 plus advance of 2pi). It has the same measured
Chris@119 165 // phase as last time around, -pi/2, which is consistent with
Chris@119 166 // the expected phase, so the unwrapped phase is 3pi/2.
Chris@129 167 //
Chris@129 168 // * Bin 3 I don't really know about -- the magnitude here is 0,
Chris@129 169 // but we get non-zero measured phase whose sign is
Chris@129 170 // implementation-dependent
Chris@119 171 //
Chris@119 172 // * Bin 4 (Nyquist) has expected phase 4*pi, measured phase 0,
Chris@119 173 // hence error 0 and unwrapped phase 4*pi.
Chris@119 174
Chris@129 175 phase[1+3] = 0.0; // Because we aren't testing for this one
Chris@129 176 double phaseExpected1[] = { 999, 0, -M_PI, -M_PI/2, 0, 0, 999 };
Chris@117 177 COMPARE_ARRAY(phase, phaseExpected1);
Chris@117 178
Chris@117 179 double unwExpected1[] = { 999, 0, M_PI, 3*M_PI/2, 3*M_PI, 4*M_PI, 999 };
Chris@117 180 COMPARE_ARRAY(unw, unwExpected1);
Chris@117 181
Chris@119 182 pvoc.processTimeDomain(data + 16, mag + 1, phase + 1, unw + 1);
Chris@117 183
Chris@117 184 double magExpected2[] = { 999, 0, 4, 4, 0, 0, 999 };
Chris@117 185 COMPARE_ARRAY(mag, magExpected2);
Chris@117 186
Chris@117 187 double phaseExpected2[] = { 999, 0, 0, -M_PI/2, 0, 0, 999 };
Chris@117 188 COMPARE_ARRAY(phase, phaseExpected2);
Chris@117 189
Chris@117 190 double unwExpected2[] = { 999, 0, 2*M_PI, 7*M_PI/2, 6*M_PI, 8*M_PI, 999 };
Chris@117 191 COMPARE_ARRAY(unw, unwExpected2);
Chris@117 192 }
Chris@115 193
Chris@112 194 BOOST_AUTO_TEST_SUITE_END()
Chris@112 195