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