Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/webaudio/FFTBlock.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* vim:set ts=4 sw=4 sts=4 et cindent: */
3
/*
4
 * Copyright (C) 2010 Google Inc. All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met:
9
 *
10
 * 1.  Redistributions of source code must retain the above copyright
11
 *     notice, this list of conditions and the following disclaimer.
12
 * 2.  Redistributions in binary form must reproduce the above copyright
13
 *     notice, this list of conditions and the following disclaimer in the
14
 *     documentation and/or other materials provided with the distribution.
15
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16
 *     its contributors may be used to endorse or promote products derived
17
 *     from this software without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 */
30
31
#include "FFTBlock.h"
32
33
#include <complex>
34
35
namespace mozilla {
36
37
typedef std::complex<double> Complex;
38
39
#ifdef MOZ_LIBAV_FFT
40
FFmpegRDFTFuncs FFTBlock::sRDFTFuncs;
41
#endif
42
43
FFTBlock* FFTBlock::CreateInterpolatedBlock(const FFTBlock& block0, const FFTBlock& block1, double interp)
44
0
{
45
0
    FFTBlock* newBlock = new FFTBlock(block0.FFTSize());
46
0
47
0
    newBlock->InterpolateFrequencyComponents(block0, block1, interp);
48
0
49
0
    // In the time-domain, the 2nd half of the response must be zero, to avoid circular convolution aliasing...
50
0
    int fftSize = newBlock->FFTSize();
51
0
    AlignedTArray<float> buffer(fftSize);
52
0
    newBlock->GetInverseWithoutScaling(buffer.Elements());
53
0
    AudioBufferInPlaceScale(buffer.Elements(), 1.0f / fftSize, fftSize / 2);
54
0
    PodZero(buffer.Elements() + fftSize / 2, fftSize / 2);
55
0
56
0
    // Put back into frequency domain.
57
0
    newBlock->PerformFFT(buffer.Elements());
58
0
59
0
    return newBlock;
60
0
}
61
62
void FFTBlock::InterpolateFrequencyComponents(const FFTBlock& block0, const FFTBlock& block1, double interp)
63
0
{
64
0
    // FIXME : with some work, this method could be optimized
65
0
66
0
    ComplexU* dft = mOutputBuffer.Elements();
67
0
68
0
    const ComplexU* dft1 = block0.mOutputBuffer.Elements();
69
0
    const ComplexU* dft2 = block1.mOutputBuffer.Elements();
70
0
71
0
    MOZ_ASSERT(mFFTSize == block0.FFTSize());
72
0
    MOZ_ASSERT(mFFTSize == block1.FFTSize());
73
0
    double s1base = (1.0 - interp);
74
0
    double s2base = interp;
75
0
76
0
    double phaseAccum = 0.0;
77
0
    double lastPhase1 = 0.0;
78
0
    double lastPhase2 = 0.0;
79
0
80
0
    int n = mFFTSize / 2;
81
0
82
0
    dft[0].r = static_cast<float>(s1base * dft1[0].r + s2base * dft2[0].r);
83
0
    dft[n].r = static_cast<float>(s1base * dft1[n].r + s2base * dft2[n].r);
84
0
85
0
    for (int i = 1; i < n; ++i) {
86
0
        Complex c1(dft1[i].r, dft1[i].i);
87
0
        Complex c2(dft2[i].r, dft2[i].i);
88
0
89
0
        double mag1 = abs(c1);
90
0
        double mag2 = abs(c2);
91
0
92
0
        // Interpolate magnitudes in decibels
93
0
        double mag1db = 20.0 * log10(mag1);
94
0
        double mag2db = 20.0 * log10(mag2);
95
0
96
0
        double s1 = s1base;
97
0
        double s2 = s2base;
98
0
99
0
        double magdbdiff = mag1db - mag2db;
100
0
101
0
        // Empirical tweak to retain higher-frequency zeroes
102
0
        double threshold =  (i > 16) ? 5.0 : 2.0;
103
0
104
0
        if (magdbdiff < -threshold && mag1db < 0.0) {
105
0
            s1 = pow(s1, 0.75);
106
0
            s2 = 1.0 - s1;
107
0
        } else if (magdbdiff > threshold && mag2db < 0.0) {
108
0
            s2 = pow(s2, 0.75);
109
0
            s1 = 1.0 - s2;
110
0
        }
111
0
112
0
        // Average magnitude by decibels instead of linearly
113
0
        double magdb = s1 * mag1db + s2 * mag2db;
114
0
        double mag = pow(10.0, 0.05 * magdb);
115
0
116
0
        // Now, deal with phase
117
0
        double phase1 = arg(c1);
118
0
        double phase2 = arg(c2);
119
0
120
0
        double deltaPhase1 = phase1 - lastPhase1;
121
0
        double deltaPhase2 = phase2 - lastPhase2;
122
0
        lastPhase1 = phase1;
123
0
        lastPhase2 = phase2;
124
0
125
0
        // Unwrap phase deltas
126
0
        if (deltaPhase1 > M_PI)
127
0
            deltaPhase1 -= 2.0 * M_PI;
128
0
        if (deltaPhase1 < -M_PI)
129
0
            deltaPhase1 += 2.0 * M_PI;
130
0
        if (deltaPhase2 > M_PI)
131
0
            deltaPhase2 -= 2.0 * M_PI;
132
0
        if (deltaPhase2 < -M_PI)
133
0
            deltaPhase2 += 2.0 * M_PI;
134
0
135
0
        // Blend group-delays
136
0
        double deltaPhaseBlend;
137
0
138
0
        if (deltaPhase1 - deltaPhase2 > M_PI)
139
0
            deltaPhaseBlend = s1 * deltaPhase1 + s2 * (2.0 * M_PI + deltaPhase2);
140
0
        else if (deltaPhase2 - deltaPhase1 > M_PI)
141
0
            deltaPhaseBlend = s1 * (2.0 * M_PI + deltaPhase1) + s2 * deltaPhase2;
142
0
        else
143
0
            deltaPhaseBlend = s1 * deltaPhase1 + s2 * deltaPhase2;
144
0
145
0
        phaseAccum += deltaPhaseBlend;
146
0
147
0
        // Unwrap
148
0
        if (phaseAccum > M_PI)
149
0
            phaseAccum -= 2.0 * M_PI;
150
0
        if (phaseAccum < -M_PI)
151
0
            phaseAccum += 2.0 * M_PI;
152
0
153
0
        dft[i].r = static_cast<float>(mag * cos(phaseAccum));
154
0
        dft[i].i = static_cast<float>(mag * sin(phaseAccum));
155
0
    }
156
0
}
157
158
double FFTBlock::ExtractAverageGroupDelay()
159
0
{
160
0
    ComplexU* dft = mOutputBuffer.Elements();
161
0
162
0
    double aveSum = 0.0;
163
0
    double weightSum = 0.0;
164
0
    double lastPhase = 0.0;
165
0
166
0
    int halfSize = FFTSize() / 2;
167
0
168
0
    const double kSamplePhaseDelay = (2.0 * M_PI) / double(FFTSize());
169
0
170
0
    // Remove DC offset
171
0
    dft[0].r = 0.0f;
172
0
173
0
    // Calculate weighted average group delay
174
0
    for (int i = 1; i < halfSize; i++) {
175
0
        Complex c(dft[i].r, dft[i].i);
176
0
        double mag = abs(c);
177
0
        double phase = arg(c);
178
0
179
0
        double deltaPhase = phase - lastPhase;
180
0
        lastPhase = phase;
181
0
182
0
        // Unwrap
183
0
        if (deltaPhase < -M_PI)
184
0
            deltaPhase += 2.0 * M_PI;
185
0
        if (deltaPhase > M_PI)
186
0
            deltaPhase -= 2.0 * M_PI;
187
0
188
0
        aveSum += mag * deltaPhase;
189
0
        weightSum += mag;
190
0
    }
191
0
192
0
    // Note how we invert the phase delta wrt frequency since this is how group delay is defined
193
0
    double ave = aveSum / weightSum;
194
0
    double aveSampleDelay = -ave / kSamplePhaseDelay;
195
0
196
0
    // Leave 20 sample headroom (for leading edge of impulse)
197
0
    aveSampleDelay -= 20.0;
198
0
    if (aveSampleDelay <= 0.0)
199
0
        return 0.0;
200
0
201
0
    // Remove average group delay (minus 20 samples for headroom)
202
0
    AddConstantGroupDelay(-aveSampleDelay);
203
0
204
0
    return aveSampleDelay;
205
0
}
206
207
void FFTBlock::AddConstantGroupDelay(double sampleFrameDelay)
208
0
{
209
0
    int halfSize = FFTSize() / 2;
210
0
211
0
    ComplexU* dft = mOutputBuffer.Elements();
212
0
213
0
    const double kSamplePhaseDelay = (2.0 * M_PI) / double(FFTSize());
214
0
215
0
    double phaseAdj = -sampleFrameDelay * kSamplePhaseDelay;
216
0
217
0
    // Add constant group delay
218
0
    for (int i = 1; i < halfSize; i++) {
219
0
        Complex c(dft[i].r, dft[i].i);
220
0
        double mag = abs(c);
221
0
        double phase = arg(c);
222
0
223
0
        phase += i * phaseAdj;
224
0
225
0
        dft[i].r = static_cast<float>(mag * cos(phase));
226
0
        dft[i].i = static_cast<float>(mag * sin(phase));
227
0
    }
228
0
}
229
230
} // namespace mozilla