• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
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.
16  *
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.
27  */
28 
29 #include "config.h"
30 
31 #if ENABLE(WEB_AUDIO)
32 
33 #include "FFTFrame.h"
34 
35 #ifndef NDEBUG
36 #include <stdio.h>
37 #endif
38 
39 #include <wtf/Complex.h>
40 #include <wtf/MathExtras.h>
41 #include <wtf/OwnPtr.h>
42 
43 namespace WebCore {
44 
doPaddedFFT(float * data,size_t dataSize)45 void FFTFrame::doPaddedFFT(float* data, size_t dataSize)
46 {
47     // Zero-pad the impulse response
48     AudioFloatArray paddedResponse(fftSize()); // zero-initialized
49     paddedResponse.copyToRange(data, 0, dataSize);
50 
51     // Get the frequency-domain version of padded response
52     doFFT(paddedResponse.data());
53 }
54 
createInterpolatedFrame(const FFTFrame & frame1,const FFTFrame & frame2,double x)55 PassOwnPtr<FFTFrame> FFTFrame::createInterpolatedFrame(const FFTFrame& frame1, const FFTFrame& frame2, double x)
56 {
57     OwnPtr<FFTFrame> newFrame = adoptPtr(new FFTFrame(frame1.fftSize()));
58 
59     newFrame->interpolateFrequencyComponents(frame1, frame2, x);
60 
61     // In the time-domain, the 2nd half of the response must be zero, to avoid circular convolution aliasing...
62     int fftSize = newFrame->fftSize();
63     AudioFloatArray buffer(fftSize);
64     newFrame->doInverseFFT(buffer.data());
65     buffer.zeroRange(fftSize / 2, fftSize);
66 
67     // Put back into frequency domain.
68     newFrame->doFFT(buffer.data());
69 
70     return newFrame.release();
71 }
72 
interpolateFrequencyComponents(const FFTFrame & frame1,const FFTFrame & frame2,double interp)73 void FFTFrame::interpolateFrequencyComponents(const FFTFrame& frame1, const FFTFrame& frame2, double interp)
74 {
75     // FIXME : with some work, this method could be optimized
76 
77     float* realP = realData();
78     float* imagP = imagData();
79 
80     const float* realP1 = frame1.realData();
81     const float* imagP1 = frame1.imagData();
82     const float* realP2 = frame2.realData();
83     const float* imagP2 = frame2.imagData();
84 
85     m_FFTSize = frame1.fftSize();
86     m_log2FFTSize = frame1.log2FFTSize();
87 
88     double s1base = (1.0 - interp);
89     double s2base = interp;
90 
91     double phaseAccum = 0.0;
92     double lastPhase1 = 0.0;
93     double lastPhase2 = 0.0;
94 
95     realP[0] = static_cast<float>(s1base * realP1[0] + s2base * realP2[0]);
96     imagP[0] = static_cast<float>(s1base * imagP1[0] + s2base * imagP2[0]);
97 
98     int n = m_FFTSize / 2;
99 
100     for (int i = 1; i < n; ++i) {
101         Complex c1(realP1[i], imagP1[i]);
102         Complex c2(realP2[i], imagP2[i]);
103 
104         double mag1 = abs(c1);
105         double mag2 = abs(c2);
106 
107         // Interpolate magnitudes in decibels
108         double mag1db = 20.0 * log10(mag1);
109         double mag2db = 20.0 * log10(mag2);
110 
111         double s1 = s1base;
112         double s2 = s2base;
113 
114         double magdbdiff = mag1db - mag2db;
115 
116         // Empirical tweak to retain higher-frequency zeroes
117         double threshold =  (i > 16) ? 5.0 : 2.0;
118 
119         if (magdbdiff < -threshold && mag1db < 0.0) {
120             s1 = pow(s1, 0.75);
121             s2 = 1.0 - s1;
122         } else if (magdbdiff > threshold && mag2db < 0.0) {
123             s2 = pow(s2, 0.75);
124             s1 = 1.0 - s2;
125         }
126 
127         // Average magnitude by decibels instead of linearly
128         double magdb = s1 * mag1db + s2 * mag2db;
129         double mag = pow(10.0, 0.05 * magdb);
130 
131         // Now, deal with phase
132         double phase1 = arg(c1);
133         double phase2 = arg(c2);
134 
135         double deltaPhase1 = phase1 - lastPhase1;
136         double deltaPhase2 = phase2 - lastPhase2;
137         lastPhase1 = phase1;
138         lastPhase2 = phase2;
139 
140         // Unwrap phase deltas
141         if (deltaPhase1 > piDouble)
142             deltaPhase1 -= 2.0 * piDouble;
143         if (deltaPhase1 < -piDouble)
144             deltaPhase1 += 2.0 * piDouble;
145         if (deltaPhase2 > piDouble)
146             deltaPhase2 -= 2.0 * piDouble;
147         if (deltaPhase2 < -piDouble)
148             deltaPhase2 += 2.0 * piDouble;
149 
150         // Blend group-delays
151         double deltaPhaseBlend;
152 
153         if (deltaPhase1 - deltaPhase2 > piDouble)
154             deltaPhaseBlend = s1 * deltaPhase1 + s2 * (2.0 * piDouble + deltaPhase2);
155         else if (deltaPhase2 - deltaPhase1 > piDouble)
156             deltaPhaseBlend = s1 * (2.0 * piDouble + deltaPhase1) + s2 * deltaPhase2;
157         else
158             deltaPhaseBlend = s1 * deltaPhase1 + s2 * deltaPhase2;
159 
160         phaseAccum += deltaPhaseBlend;
161 
162         // Unwrap
163         if (phaseAccum > piDouble)
164             phaseAccum -= 2.0 * piDouble;
165         if (phaseAccum < -piDouble)
166             phaseAccum += 2.0 * piDouble;
167 
168         Complex c = complexFromMagnitudePhase(mag, phaseAccum);
169 
170         realP[i] = static_cast<float>(c.real());
171         imagP[i] = static_cast<float>(c.imag());
172     }
173 }
174 
extractAverageGroupDelay()175 double FFTFrame::extractAverageGroupDelay()
176 {
177     float* realP = realData();
178     float* imagP = imagData();
179 
180     double aveSum = 0.0;
181     double weightSum = 0.0;
182     double lastPhase = 0.0;
183 
184     int halfSize = fftSize() / 2;
185 
186     const double kSamplePhaseDelay = (2.0 * piDouble) / double(fftSize());
187 
188     // Calculate weighted average group delay
189     for (int i = 0; i < halfSize; i++) {
190         Complex c(realP[i], imagP[i]);
191         double mag = abs(c);
192         double phase = arg(c);
193 
194         double deltaPhase = phase - lastPhase;
195         lastPhase = phase;
196 
197         // Unwrap
198         if (deltaPhase < -piDouble)
199             deltaPhase += 2.0 * piDouble;
200         if (deltaPhase > piDouble)
201             deltaPhase -= 2.0 * piDouble;
202 
203         aveSum += mag * deltaPhase;
204         weightSum += mag;
205     }
206 
207     // Note how we invert the phase delta wrt frequency since this is how group delay is defined
208     double ave = aveSum / weightSum;
209     double aveSampleDelay = -ave / kSamplePhaseDelay;
210 
211     // Leave 20 sample headroom (for leading edge of impulse)
212     if (aveSampleDelay > 20.0)
213         aveSampleDelay -= 20.0;
214 
215     // Remove average group delay (minus 20 samples for headroom)
216     addConstantGroupDelay(-aveSampleDelay);
217 
218     // Remove DC offset
219     realP[0] = 0.0f;
220 
221     return aveSampleDelay;
222 }
223 
addConstantGroupDelay(double sampleFrameDelay)224 void FFTFrame::addConstantGroupDelay(double sampleFrameDelay)
225 {
226     int halfSize = fftSize() / 2;
227 
228     float* realP = realData();
229     float* imagP = imagData();
230 
231     const double kSamplePhaseDelay = (2.0 * piDouble) / double(fftSize());
232 
233     double phaseAdj = -sampleFrameDelay * kSamplePhaseDelay;
234 
235     // Add constant group delay
236     for (int i = 1; i < halfSize; i++) {
237         Complex c(realP[i], imagP[i]);
238         double mag = abs(c);
239         double phase = arg(c);
240 
241         phase += i * phaseAdj;
242 
243         Complex c2 = complexFromMagnitudePhase(mag, phase);
244 
245         realP[i] = static_cast<float>(c2.real());
246         imagP[i] = static_cast<float>(c2.imag());
247     }
248 }
249 
250 #ifndef NDEBUG
print()251 void FFTFrame::print()
252 {
253     FFTFrame& frame = *this;
254     float* realP = frame.realData();
255     float* imagP = frame.imagData();
256     printf("**** \n");
257     printf("DC = %f : nyquist = %f\n", realP[0], imagP[0]);
258 
259     int n = m_FFTSize / 2;
260 
261     for (int i = 1; i < n; i++) {
262         double mag = sqrt(realP[i] * realP[i] + imagP[i] * imagP[i]);
263         double phase = atan2(realP[i], imagP[i]);
264 
265         printf("[%d] (%f %f)\n", i, mag, phase);
266     }
267     printf("****\n");
268 }
269 #endif // NDEBUG
270 
271 } // namespace WebCore
272 
273 #endif // ENABLE(WEB_AUDIO)
274