2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include "platform/audio/FFTFrame.h"
35 #include "platform/audio/VectorMath.h"
41 #include "platform/Logging.h"
42 #include "wtf/Complex.h"
43 #include "wtf/MathExtras.h"
44 #include "wtf/OwnPtr.h"
48 void FFTFrame::doPaddedFFT(const float* data, size_t dataSize)
50 // Zero-pad the impulse response
51 AudioFloatArray paddedResponse(fftSize()); // zero-initialized
52 paddedResponse.copyToRange(data, 0, dataSize);
54 // Get the frequency-domain version of padded response
55 doFFT(paddedResponse.data());
58 PassOwnPtr<FFTFrame> FFTFrame::createInterpolatedFrame(const FFTFrame& frame1, const FFTFrame& frame2, double x)
60 OwnPtr<FFTFrame> newFrame = adoptPtr(new FFTFrame(frame1.fftSize()));
62 newFrame->interpolateFrequencyComponents(frame1, frame2, x);
64 // In the time-domain, the 2nd half of the response must be zero, to avoid circular convolution aliasing...
65 int fftSize = newFrame->fftSize();
66 AudioFloatArray buffer(fftSize);
67 newFrame->doInverseFFT(buffer.data());
68 buffer.zeroRange(fftSize / 2, fftSize);
70 // Put back into frequency domain.
71 newFrame->doFFT(buffer.data());
73 return newFrame.release();
76 void FFTFrame::interpolateFrequencyComponents(const FFTFrame& frame1, const FFTFrame& frame2, double interp)
78 // FIXME : with some work, this method could be optimized
80 float* realP = realData();
81 float* imagP = imagData();
83 const float* realP1 = frame1.realData();
84 const float* imagP1 = frame1.imagData();
85 const float* realP2 = frame2.realData();
86 const float* imagP2 = frame2.imagData();
88 m_FFTSize = frame1.fftSize();
89 m_log2FFTSize = frame1.log2FFTSize();
91 double s1base = (1.0 - interp);
92 double s2base = interp;
94 double phaseAccum = 0.0;
95 double lastPhase1 = 0.0;
96 double lastPhase2 = 0.0;
98 realP[0] = static_cast<float>(s1base * realP1[0] + s2base * realP2[0]);
99 imagP[0] = static_cast<float>(s1base * imagP1[0] + s2base * imagP2[0]);
101 int n = m_FFTSize / 2;
103 for (int i = 1; i < n; ++i) {
104 Complex c1(realP1[i], imagP1[i]);
105 Complex c2(realP2[i], imagP2[i]);
107 double mag1 = abs(c1);
108 double mag2 = abs(c2);
110 // Interpolate magnitudes in decibels
111 double mag1db = 20.0 * log10(mag1);
112 double mag2db = 20.0 * log10(mag2);
117 double magdbdiff = mag1db - mag2db;
119 // Empirical tweak to retain higher-frequency zeroes
120 double threshold = (i > 16) ? 5.0 : 2.0;
122 if (magdbdiff < -threshold && mag1db < 0.0) {
125 } else if (magdbdiff > threshold && mag2db < 0.0) {
130 // Average magnitude by decibels instead of linearly
131 double magdb = s1 * mag1db + s2 * mag2db;
132 double mag = pow(10.0, 0.05 * magdb);
134 // Now, deal with phase
135 double phase1 = arg(c1);
136 double phase2 = arg(c2);
138 double deltaPhase1 = phase1 - lastPhase1;
139 double deltaPhase2 = phase2 - lastPhase2;
143 // Unwrap phase deltas
144 if (deltaPhase1 > piDouble)
145 deltaPhase1 -= twoPiDouble;
146 if (deltaPhase1 < -piDouble)
147 deltaPhase1 += twoPiDouble;
148 if (deltaPhase2 > piDouble)
149 deltaPhase2 -= twoPiDouble;
150 if (deltaPhase2 < -piDouble)
151 deltaPhase2 += twoPiDouble;
153 // Blend group-delays
154 double deltaPhaseBlend;
156 if (deltaPhase1 - deltaPhase2 > piDouble)
157 deltaPhaseBlend = s1 * deltaPhase1 + s2 * (twoPiDouble + deltaPhase2);
158 else if (deltaPhase2 - deltaPhase1 > piDouble)
159 deltaPhaseBlend = s1 * (twoPiDouble + deltaPhase1) + s2 * deltaPhase2;
161 deltaPhaseBlend = s1 * deltaPhase1 + s2 * deltaPhase2;
163 phaseAccum += deltaPhaseBlend;
166 if (phaseAccum > piDouble)
167 phaseAccum -= twoPiDouble;
168 if (phaseAccum < -piDouble)
169 phaseAccum += twoPiDouble;
171 Complex c = complexFromMagnitudePhase(mag, phaseAccum);
173 realP[i] = static_cast<float>(c.real());
174 imagP[i] = static_cast<float>(c.imag());
178 double FFTFrame::extractAverageGroupDelay()
180 float* realP = realData();
181 float* imagP = imagData();
184 double weightSum = 0.0;
185 double lastPhase = 0.0;
187 int halfSize = fftSize() / 2;
189 const double kSamplePhaseDelay = (twoPiDouble) / double(fftSize());
191 // Calculate weighted average group delay
192 for (int i = 0; i < halfSize; i++) {
193 Complex c(realP[i], imagP[i]);
195 double phase = arg(c);
197 double deltaPhase = phase - lastPhase;
201 if (deltaPhase < -piDouble)
202 deltaPhase += twoPiDouble;
203 if (deltaPhase > piDouble)
204 deltaPhase -= twoPiDouble;
206 aveSum += mag * deltaPhase;
210 // Note how we invert the phase delta wrt frequency since this is how group delay is defined
211 double ave = aveSum / weightSum;
212 double aveSampleDelay = -ave / kSamplePhaseDelay;
214 // Leave 20 sample headroom (for leading edge of impulse)
215 if (aveSampleDelay > 20.0)
216 aveSampleDelay -= 20.0;
218 // Remove average group delay (minus 20 samples for headroom)
219 addConstantGroupDelay(-aveSampleDelay);
224 return aveSampleDelay;
227 void FFTFrame::addConstantGroupDelay(double sampleFrameDelay)
229 int halfSize = fftSize() / 2;
231 float* realP = realData();
232 float* imagP = imagData();
234 const double kSamplePhaseDelay = (twoPiDouble) / double(fftSize());
236 double phaseAdj = -sampleFrameDelay * kSamplePhaseDelay;
238 // Add constant group delay
239 for (int i = 1; i < halfSize; i++) {
240 Complex c(realP[i], imagP[i]);
242 double phase = arg(c);
244 phase += i * phaseAdj;
246 Complex c2 = complexFromMagnitudePhase(mag, phase);
248 realP[i] = static_cast<float>(c2.real());
249 imagP[i] = static_cast<float>(c2.imag());
253 void FFTFrame::multiply(const FFTFrame& frame)
255 FFTFrame& frame1 = *this;
256 FFTFrame& frame2 = const_cast<FFTFrame&>(frame);
258 float* realP1 = frame1.realData();
259 float* imagP1 = frame1.imagData();
260 const float* realP2 = frame2.realData();
261 const float* imagP2 = frame2.imagData();
263 unsigned halfSize = fftSize() / 2;
264 float real0 = realP1[0];
265 float imag0 = imagP1[0];
267 VectorMath::zvmul(realP1, imagP1, realP2, imagP2, realP1, imagP1, halfSize);
269 // Multiply the packed DC/nyquist component
270 realP1[0] = real0 * realP2[0];
271 imagP1[0] = imag0 * imagP2[0];
275 void FFTFrame::print()
277 FFTFrame& frame = *this;
278 float* realP = frame.realData();
279 float* imagP = frame.imagData();
280 WTF_LOG(WebAudio, "**** \n");
281 WTF_LOG(WebAudio, "DC = %f : nyquist = %f\n", realP[0], imagP[0]);
283 int n = m_FFTSize / 2;
285 for (int i = 1; i < n; i++) {
286 double mag = sqrt(realP[i] * realP[i] + imagP[i] * imagP[i]);
287 double phase = atan2(realP[i], imagP[i]);
289 WTF_LOG(WebAudio, "[%d] (%f %f)\n", i, mag, phase);
291 WTF_LOG(WebAudio, "****\n");
297 #endif // ENABLE(WEB_AUDIO)