content/media/webaudio/FFTBlock.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial