Tue, 06 Jan 2015 21:39:09 +0100
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 | /* |
michael@0 | 2 | * Copyright (C) 2010 Google Inc. All rights reserved. |
michael@0 | 3 | * |
michael@0 | 4 | * Redistribution and use in source and binary forms, with or without |
michael@0 | 5 | * modification, are permitted provided that the following conditions |
michael@0 | 6 | * are met: |
michael@0 | 7 | * |
michael@0 | 8 | * 1. Redistributions of source code must retain the above copyright |
michael@0 | 9 | * notice, this list of conditions and the following disclaimer. |
michael@0 | 10 | * 2. Redistributions in binary form must reproduce the above copyright |
michael@0 | 11 | * notice, this list of conditions and the following disclaimer in the |
michael@0 | 12 | * documentation and/or other materials provided with the distribution. |
michael@0 | 13 | * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
michael@0 | 14 | * its contributors may be used to endorse or promote products derived |
michael@0 | 15 | * from this software without specific prior written permission. |
michael@0 | 16 | * |
michael@0 | 17 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
michael@0 | 18 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
michael@0 | 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
michael@0 | 20 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
michael@0 | 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
michael@0 | 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
michael@0 | 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
michael@0 | 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
michael@0 | 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
michael@0 | 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
michael@0 | 27 | */ |
michael@0 | 28 | |
michael@0 | 29 | #include "HRTFElevation.h" |
michael@0 | 30 | |
michael@0 | 31 | #include "speex/speex_resampler.h" |
michael@0 | 32 | #include "mozilla/PodOperations.h" |
michael@0 | 33 | #include "AudioSampleFormat.h" |
michael@0 | 34 | |
michael@0 | 35 | #include "IRC_Composite_C_R0195-incl.cpp" |
michael@0 | 36 | |
michael@0 | 37 | using namespace std; |
michael@0 | 38 | using namespace mozilla; |
michael@0 | 39 | |
michael@0 | 40 | namespace WebCore { |
michael@0 | 41 | |
michael@0 | 42 | const int elevationSpacing = irc_composite_c_r0195_elevation_interval; |
michael@0 | 43 | const int firstElevation = irc_composite_c_r0195_first_elevation; |
michael@0 | 44 | const int numberOfElevations = MOZ_ARRAY_LENGTH(irc_composite_c_r0195); |
michael@0 | 45 | |
michael@0 | 46 | const unsigned HRTFElevation::NumberOfTotalAzimuths = 360 / 15 * 8; |
michael@0 | 47 | |
michael@0 | 48 | const int rawSampleRate = irc_composite_c_r0195_sample_rate; |
michael@0 | 49 | |
michael@0 | 50 | // Number of frames in an individual impulse response. |
michael@0 | 51 | const size_t ResponseFrameSize = 256; |
michael@0 | 52 | |
michael@0 | 53 | size_t HRTFElevation::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
michael@0 | 54 | { |
michael@0 | 55 | size_t amount = aMallocSizeOf(this); |
michael@0 | 56 | |
michael@0 | 57 | amount += m_kernelListL.SizeOfExcludingThis(aMallocSizeOf); |
michael@0 | 58 | for (size_t i = 0; i < m_kernelListL.Length(); i++) { |
michael@0 | 59 | amount += m_kernelListL[i]->sizeOfIncludingThis(aMallocSizeOf); |
michael@0 | 60 | } |
michael@0 | 61 | |
michael@0 | 62 | return amount; |
michael@0 | 63 | } |
michael@0 | 64 | |
michael@0 | 65 | size_t HRTFElevation::fftSizeForSampleRate(float sampleRate) |
michael@0 | 66 | { |
michael@0 | 67 | // The IRCAM HRTF impulse responses were 512 sample-frames @44.1KHz, |
michael@0 | 68 | // but these have been truncated to 256 samples. |
michael@0 | 69 | // An FFT-size of twice impulse response size is used (for convolution). |
michael@0 | 70 | // So for sample rates of 44.1KHz an FFT size of 512 is good. |
michael@0 | 71 | // We double the FFT-size only for sample rates at least double this. |
michael@0 | 72 | // If the FFT size is too large then the impulse response will be padded |
michael@0 | 73 | // with zeros without the fade-out provided by HRTFKernel. |
michael@0 | 74 | MOZ_ASSERT(sampleRate > 1.0 && sampleRate < 1048576.0); |
michael@0 | 75 | |
michael@0 | 76 | // This is the size if we were to use all raw response samples. |
michael@0 | 77 | unsigned resampledLength = |
michael@0 | 78 | floorf(ResponseFrameSize * sampleRate / rawSampleRate); |
michael@0 | 79 | // Keep things semi-sane, with max FFT size of 1024 and minimum of 4. |
michael@0 | 80 | // "size |= 3" ensures a minimum of 4 (with the size++ below) and sets the |
michael@0 | 81 | // 2 least significant bits for rounding up to the next power of 2 below. |
michael@0 | 82 | unsigned size = min(resampledLength, 1023U); |
michael@0 | 83 | size |= 3; |
michael@0 | 84 | // Round up to the next power of 2, making the FFT size no more than twice |
michael@0 | 85 | // the impulse response length. This doubles size for values that are |
michael@0 | 86 | // already powers of 2. This works by filling in 7 bits to right of the |
michael@0 | 87 | // most significant bit. The most significant bit is no greater than |
michael@0 | 88 | // 1 << 9, and the least significant 2 bits were already set above. |
michael@0 | 89 | size |= (size >> 1); |
michael@0 | 90 | size |= (size >> 2); |
michael@0 | 91 | size |= (size >> 4); |
michael@0 | 92 | size++; |
michael@0 | 93 | MOZ_ASSERT((size & (size - 1)) == 0); |
michael@0 | 94 | |
michael@0 | 95 | return size; |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | nsReturnRef<HRTFKernel> HRTFElevation::calculateKernelForAzimuthElevation(int azimuth, int elevation, SpeexResamplerState* resampler, float sampleRate) |
michael@0 | 99 | { |
michael@0 | 100 | int elevationIndex = (elevation - firstElevation) / elevationSpacing; |
michael@0 | 101 | MOZ_ASSERT(elevationIndex >= 0 && elevationIndex <= numberOfElevations); |
michael@0 | 102 | |
michael@0 | 103 | int numberOfAzimuths = irc_composite_c_r0195[elevationIndex].count; |
michael@0 | 104 | int azimuthSpacing = 360 / numberOfAzimuths; |
michael@0 | 105 | MOZ_ASSERT(numberOfAzimuths * azimuthSpacing == 360); |
michael@0 | 106 | |
michael@0 | 107 | int azimuthIndex = azimuth / azimuthSpacing; |
michael@0 | 108 | MOZ_ASSERT(azimuthIndex * azimuthSpacing == azimuth); |
michael@0 | 109 | |
michael@0 | 110 | const int16_t (&impulse_response_data)[ResponseFrameSize] = |
michael@0 | 111 | irc_composite_c_r0195[elevationIndex].azimuths[azimuthIndex]; |
michael@0 | 112 | |
michael@0 | 113 | // When libspeex_resampler is compiled with FIXED_POINT, samples in |
michael@0 | 114 | // speex_resampler_process_float are rounded directly to int16_t, which |
michael@0 | 115 | // only works well if the floats are in the range +/-32767. On such |
michael@0 | 116 | // platforms it's better to resample before converting to float anyway. |
michael@0 | 117 | #ifdef MOZ_SAMPLE_TYPE_S16 |
michael@0 | 118 | # define RESAMPLER_PROCESS speex_resampler_process_int |
michael@0 | 119 | const int16_t* response = impulse_response_data; |
michael@0 | 120 | const int16_t* resampledResponse; |
michael@0 | 121 | #else |
michael@0 | 122 | # define RESAMPLER_PROCESS speex_resampler_process_float |
michael@0 | 123 | float response[ResponseFrameSize]; |
michael@0 | 124 | ConvertAudioSamples(impulse_response_data, response, ResponseFrameSize); |
michael@0 | 125 | float* resampledResponse; |
michael@0 | 126 | #endif |
michael@0 | 127 | |
michael@0 | 128 | // Note that depending on the fftSize returned by the panner, we may be truncating the impulse response. |
michael@0 | 129 | const size_t resampledResponseLength = fftSizeForSampleRate(sampleRate) / 2; |
michael@0 | 130 | |
michael@0 | 131 | nsAutoTArray<AudioDataValue, 2 * ResponseFrameSize> resampled; |
michael@0 | 132 | if (sampleRate == rawSampleRate) { |
michael@0 | 133 | resampledResponse = response; |
michael@0 | 134 | MOZ_ASSERT(resampledResponseLength == ResponseFrameSize); |
michael@0 | 135 | } else { |
michael@0 | 136 | resampled.SetLength(resampledResponseLength); |
michael@0 | 137 | resampledResponse = resampled.Elements(); |
michael@0 | 138 | speex_resampler_skip_zeros(resampler); |
michael@0 | 139 | |
michael@0 | 140 | // Feed the input buffer into the resampler. |
michael@0 | 141 | spx_uint32_t in_len = ResponseFrameSize; |
michael@0 | 142 | spx_uint32_t out_len = resampled.Length(); |
michael@0 | 143 | RESAMPLER_PROCESS(resampler, 0, response, &in_len, |
michael@0 | 144 | resampled.Elements(), &out_len); |
michael@0 | 145 | |
michael@0 | 146 | if (out_len < resampled.Length()) { |
michael@0 | 147 | // The input should have all been processed. |
michael@0 | 148 | MOZ_ASSERT(in_len == ResponseFrameSize); |
michael@0 | 149 | // Feed in zeros get the data remaining in the resampler. |
michael@0 | 150 | spx_uint32_t out_index = out_len; |
michael@0 | 151 | in_len = speex_resampler_get_input_latency(resampler); |
michael@0 | 152 | out_len = resampled.Length() - out_index; |
michael@0 | 153 | RESAMPLER_PROCESS(resampler, 0, nullptr, &in_len, |
michael@0 | 154 | resampled.Elements() + out_index, &out_len); |
michael@0 | 155 | out_index += out_len; |
michael@0 | 156 | // There may be some uninitialized samples remaining for very low |
michael@0 | 157 | // sample rates. |
michael@0 | 158 | PodZero(resampled.Elements() + out_index, |
michael@0 | 159 | resampled.Length() - out_index); |
michael@0 | 160 | } |
michael@0 | 161 | |
michael@0 | 162 | speex_resampler_reset_mem(resampler); |
michael@0 | 163 | } |
michael@0 | 164 | |
michael@0 | 165 | #ifdef MOZ_SAMPLE_TYPE_S16 |
michael@0 | 166 | nsAutoTArray<float, 2 * ResponseFrameSize> floatArray; |
michael@0 | 167 | floatArray.SetLength(resampledResponseLength); |
michael@0 | 168 | float *floatResponse = floatArray.Elements(); |
michael@0 | 169 | ConvertAudioSamples(resampledResponse, |
michael@0 | 170 | floatResponse, resampledResponseLength); |
michael@0 | 171 | #else |
michael@0 | 172 | float *floatResponse = resampledResponse; |
michael@0 | 173 | #endif |
michael@0 | 174 | #undef RESAMPLER_PROCESS |
michael@0 | 175 | |
michael@0 | 176 | return HRTFKernel::create(floatResponse, resampledResponseLength, sampleRate); |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | // The range of elevations for the IRCAM impulse responses varies depending on azimuth, but the minimum elevation appears to always be -45. |
michael@0 | 180 | // |
michael@0 | 181 | // Here's how it goes: |
michael@0 | 182 | static int maxElevations[] = { |
michael@0 | 183 | // Azimuth |
michael@0 | 184 | // |
michael@0 | 185 | 90, // 0 |
michael@0 | 186 | 45, // 15 |
michael@0 | 187 | 60, // 30 |
michael@0 | 188 | 45, // 45 |
michael@0 | 189 | 75, // 60 |
michael@0 | 190 | 45, // 75 |
michael@0 | 191 | 60, // 90 |
michael@0 | 192 | 45, // 105 |
michael@0 | 193 | 75, // 120 |
michael@0 | 194 | 45, // 135 |
michael@0 | 195 | 60, // 150 |
michael@0 | 196 | 45, // 165 |
michael@0 | 197 | 75, // 180 |
michael@0 | 198 | 45, // 195 |
michael@0 | 199 | 60, // 210 |
michael@0 | 200 | 45, // 225 |
michael@0 | 201 | 75, // 240 |
michael@0 | 202 | 45, // 255 |
michael@0 | 203 | 60, // 270 |
michael@0 | 204 | 45, // 285 |
michael@0 | 205 | 75, // 300 |
michael@0 | 206 | 45, // 315 |
michael@0 | 207 | 60, // 330 |
michael@0 | 208 | 45 // 345 |
michael@0 | 209 | }; |
michael@0 | 210 | |
michael@0 | 211 | nsReturnRef<HRTFElevation> HRTFElevation::createBuiltin(int elevation, float sampleRate) |
michael@0 | 212 | { |
michael@0 | 213 | if (elevation < firstElevation || |
michael@0 | 214 | elevation > firstElevation + numberOfElevations * elevationSpacing || |
michael@0 | 215 | (elevation / elevationSpacing) * elevationSpacing != elevation) |
michael@0 | 216 | return nsReturnRef<HRTFElevation>(); |
michael@0 | 217 | |
michael@0 | 218 | // Spacing, in degrees, between every azimuth loaded from resource. |
michael@0 | 219 | // Some elevations do not have data for all these intervals. |
michael@0 | 220 | // See maxElevations. |
michael@0 | 221 | static const unsigned AzimuthSpacing = 15; |
michael@0 | 222 | static const unsigned NumberOfRawAzimuths = 360 / AzimuthSpacing; |
michael@0 | 223 | static_assert(AzimuthSpacing * NumberOfRawAzimuths == 360, |
michael@0 | 224 | "Not a multiple"); |
michael@0 | 225 | static const unsigned InterpolationFactor = |
michael@0 | 226 | NumberOfTotalAzimuths / NumberOfRawAzimuths; |
michael@0 | 227 | static_assert(NumberOfTotalAzimuths == |
michael@0 | 228 | NumberOfRawAzimuths * InterpolationFactor, "Not a multiple"); |
michael@0 | 229 | |
michael@0 | 230 | HRTFKernelList kernelListL; |
michael@0 | 231 | kernelListL.SetLength(NumberOfTotalAzimuths); |
michael@0 | 232 | |
michael@0 | 233 | SpeexResamplerState* resampler = sampleRate == rawSampleRate ? nullptr : |
michael@0 | 234 | speex_resampler_init(1, rawSampleRate, sampleRate, |
michael@0 | 235 | SPEEX_RESAMPLER_QUALITY_DEFAULT, nullptr); |
michael@0 | 236 | |
michael@0 | 237 | // Load convolution kernels from HRTF files. |
michael@0 | 238 | int interpolatedIndex = 0; |
michael@0 | 239 | for (unsigned rawIndex = 0; rawIndex < NumberOfRawAzimuths; ++rawIndex) { |
michael@0 | 240 | // Don't let elevation exceed maximum for this azimuth. |
michael@0 | 241 | int maxElevation = maxElevations[rawIndex]; |
michael@0 | 242 | int actualElevation = min(elevation, maxElevation); |
michael@0 | 243 | |
michael@0 | 244 | kernelListL[interpolatedIndex] = calculateKernelForAzimuthElevation(rawIndex * AzimuthSpacing, actualElevation, resampler, sampleRate); |
michael@0 | 245 | |
michael@0 | 246 | interpolatedIndex += InterpolationFactor; |
michael@0 | 247 | } |
michael@0 | 248 | |
michael@0 | 249 | if (resampler) |
michael@0 | 250 | speex_resampler_destroy(resampler); |
michael@0 | 251 | |
michael@0 | 252 | // Now go back and interpolate intermediate azimuth values. |
michael@0 | 253 | for (unsigned i = 0; i < NumberOfTotalAzimuths; i += InterpolationFactor) { |
michael@0 | 254 | int j = (i + InterpolationFactor) % NumberOfTotalAzimuths; |
michael@0 | 255 | |
michael@0 | 256 | // Create the interpolated convolution kernels and delays. |
michael@0 | 257 | for (unsigned jj = 1; jj < InterpolationFactor; ++jj) { |
michael@0 | 258 | float x = float(jj) / float(InterpolationFactor); // interpolate from 0 -> 1 |
michael@0 | 259 | |
michael@0 | 260 | kernelListL[i + jj] = HRTFKernel::createInterpolatedKernel(kernelListL[i], kernelListL[j], x); |
michael@0 | 261 | } |
michael@0 | 262 | } |
michael@0 | 263 | |
michael@0 | 264 | return nsReturnRef<HRTFElevation>(new HRTFElevation(&kernelListL, elevation, sampleRate)); |
michael@0 | 265 | } |
michael@0 | 266 | |
michael@0 | 267 | nsReturnRef<HRTFElevation> HRTFElevation::createByInterpolatingSlices(HRTFElevation* hrtfElevation1, HRTFElevation* hrtfElevation2, float x, float sampleRate) |
michael@0 | 268 | { |
michael@0 | 269 | MOZ_ASSERT(hrtfElevation1 && hrtfElevation2); |
michael@0 | 270 | if (!hrtfElevation1 || !hrtfElevation2) |
michael@0 | 271 | return nsReturnRef<HRTFElevation>(); |
michael@0 | 272 | |
michael@0 | 273 | MOZ_ASSERT(x >= 0.0 && x < 1.0); |
michael@0 | 274 | |
michael@0 | 275 | HRTFKernelList kernelListL; |
michael@0 | 276 | kernelListL.SetLength(NumberOfTotalAzimuths); |
michael@0 | 277 | |
michael@0 | 278 | const HRTFKernelList& kernelListL1 = hrtfElevation1->kernelListL(); |
michael@0 | 279 | const HRTFKernelList& kernelListL2 = hrtfElevation2->kernelListL(); |
michael@0 | 280 | |
michael@0 | 281 | // Interpolate kernels of corresponding azimuths of the two elevations. |
michael@0 | 282 | for (unsigned i = 0; i < NumberOfTotalAzimuths; ++i) { |
michael@0 | 283 | kernelListL[i] = HRTFKernel::createInterpolatedKernel(kernelListL1[i], kernelListL2[i], x); |
michael@0 | 284 | } |
michael@0 | 285 | |
michael@0 | 286 | // Interpolate elevation angle. |
michael@0 | 287 | double angle = (1.0 - x) * hrtfElevation1->elevationAngle() + x * hrtfElevation2->elevationAngle(); |
michael@0 | 288 | |
michael@0 | 289 | return nsReturnRef<HRTFElevation>(new HRTFElevation(&kernelListL, static_cast<int>(angle), sampleRate)); |
michael@0 | 290 | } |
michael@0 | 291 | |
michael@0 | 292 | void HRTFElevation::getKernelsFromAzimuth(double azimuthBlend, unsigned azimuthIndex, HRTFKernel* &kernelL, HRTFKernel* &kernelR, double& frameDelayL, double& frameDelayR) |
michael@0 | 293 | { |
michael@0 | 294 | bool checkAzimuthBlend = azimuthBlend >= 0.0 && azimuthBlend < 1.0; |
michael@0 | 295 | MOZ_ASSERT(checkAzimuthBlend); |
michael@0 | 296 | if (!checkAzimuthBlend) |
michael@0 | 297 | azimuthBlend = 0.0; |
michael@0 | 298 | |
michael@0 | 299 | unsigned numKernels = m_kernelListL.Length(); |
michael@0 | 300 | |
michael@0 | 301 | bool isIndexGood = azimuthIndex < numKernels; |
michael@0 | 302 | MOZ_ASSERT(isIndexGood); |
michael@0 | 303 | if (!isIndexGood) { |
michael@0 | 304 | kernelL = 0; |
michael@0 | 305 | kernelR = 0; |
michael@0 | 306 | return; |
michael@0 | 307 | } |
michael@0 | 308 | |
michael@0 | 309 | // Return the left and right kernels, |
michael@0 | 310 | // using symmetry to produce the right kernel. |
michael@0 | 311 | kernelL = m_kernelListL[azimuthIndex]; |
michael@0 | 312 | int azimuthIndexR = (numKernels - azimuthIndex) % numKernels; |
michael@0 | 313 | kernelR = m_kernelListL[azimuthIndexR]; |
michael@0 | 314 | |
michael@0 | 315 | frameDelayL = kernelL->frameDelay(); |
michael@0 | 316 | frameDelayR = kernelR->frameDelay(); |
michael@0 | 317 | |
michael@0 | 318 | int azimuthIndex2L = (azimuthIndex + 1) % numKernels; |
michael@0 | 319 | double frameDelay2L = m_kernelListL[azimuthIndex2L]->frameDelay(); |
michael@0 | 320 | int azimuthIndex2R = (numKernels - azimuthIndex2L) % numKernels; |
michael@0 | 321 | double frameDelay2R = m_kernelListL[azimuthIndex2R]->frameDelay(); |
michael@0 | 322 | |
michael@0 | 323 | // Linearly interpolate delays. |
michael@0 | 324 | frameDelayL = (1.0 - azimuthBlend) * frameDelayL + azimuthBlend * frameDelay2L; |
michael@0 | 325 | frameDelayR = (1.0 - azimuthBlend) * frameDelayR + azimuthBlend * frameDelay2R; |
michael@0 | 326 | } |
michael@0 | 327 | |
michael@0 | 328 | } // namespace WebCore |