content/media/webaudio/blink/PeriodicWave.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.

     1 /*
     2  * Copyright (C) 2012 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  */
    29 #include "PeriodicWave.h"
    30 #include <algorithm>
    31 #include <cmath>
    32 #include "mozilla/FFTBlock.h"
    34 const unsigned PeriodicWaveSize = 4096; // This must be a power of two.
    35 const unsigned NumberOfRanges = 36; // There should be 3 * log2(PeriodicWaveSize) 1/3 octave ranges.
    36 const float CentsPerRange = 1200 / 3; // 1/3 Octave.
    38 using namespace mozilla;
    39 using mozilla::dom::OscillatorType;
    41 namespace WebCore {
    43 PeriodicWave* PeriodicWave::create(float sampleRate,
    44                                    const float* real,
    45                                    const float* imag,
    46                                    size_t numberOfComponents)
    47 {
    48     bool isGood = real && imag && numberOfComponents > 0 &&
    49          numberOfComponents <= PeriodicWaveSize;
    50     MOZ_ASSERT(isGood);
    51     if (isGood) {
    52         PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
    53         periodicWave->createBandLimitedTables(real, imag, numberOfComponents);
    54         return periodicWave;
    55     }
    56     return 0;
    57 }
    59 PeriodicWave* PeriodicWave::createSine(float sampleRate)
    60 {
    61       PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
    62           periodicWave->generateBasicWaveform(OscillatorType::Sine);
    63               return periodicWave;
    64 }
    66 PeriodicWave* PeriodicWave::createSquare(float sampleRate)
    67 {
    68       PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
    69           periodicWave->generateBasicWaveform(OscillatorType::Square);
    70               return periodicWave;
    71 }
    73 PeriodicWave* PeriodicWave::createSawtooth(float sampleRate)
    74 {
    75       PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
    76           periodicWave->generateBasicWaveform(OscillatorType::Sawtooth);
    77               return periodicWave;
    78 }
    80 PeriodicWave* PeriodicWave::createTriangle(float sampleRate)
    81 {
    82       PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
    83           periodicWave->generateBasicWaveform(OscillatorType::Triangle);
    84               return periodicWave;
    85 }
    87 PeriodicWave::PeriodicWave(float sampleRate)
    88     : m_sampleRate(sampleRate)
    89     , m_periodicWaveSize(PeriodicWaveSize)
    90     , m_numberOfRanges(NumberOfRanges)
    91     , m_centsPerRange(CentsPerRange)
    92 {
    93     float nyquist = 0.5 * m_sampleRate;
    94     m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
    95     m_rateScale = m_periodicWaveSize / m_sampleRate;
    96 }
    98 size_t PeriodicWave::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
    99 {
   100     size_t amount = aMallocSizeOf(this);
   102     amount += m_bandLimitedTables.SizeOfExcludingThis(aMallocSizeOf);
   103     for (size_t i = 0; i < m_bandLimitedTables.Length(); i++) {
   104         if (m_bandLimitedTables[i]) {
   105             amount += m_bandLimitedTables[i]->SizeOfIncludingThis(aMallocSizeOf);
   106         }
   107     }
   109     return amount;
   110 }
   112 void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor)
   113 {
   114     // Negative frequencies are allowed, in which case we alias
   115     // to the positive frequency.
   116     fundamentalFrequency = fabsf(fundamentalFrequency);
   118     // Calculate the pitch range.
   119     float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5;
   120     float centsAboveLowestFrequency = logf(ratio)/logf(2.0f) * 1200;
   122     // Add one to round-up to the next range just in time to truncate
   123     // partials before aliasing occurs.
   124     float pitchRange = 1 + centsAboveLowestFrequency / m_centsPerRange;
   126     pitchRange = std::max(pitchRange, 0.0f);
   127     pitchRange = std::min(pitchRange, static_cast<float>(m_numberOfRanges - 1));
   129     // The words "lower" and "higher" refer to the table data having
   130     // the lower and higher numbers of partials. It's a little confusing
   131     // since the range index gets larger the more partials we cull out.
   132     // So the lower table data will have a larger range index.
   133     unsigned rangeIndex1 = static_cast<unsigned>(pitchRange);
   134     unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1;
   136     lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements();
   137     higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements();
   139     // Ranges from 0 -> 1 to interpolate between lower -> higher.
   140     tableInterpolationFactor = pitchRange - rangeIndex1;
   141 }
   143 unsigned PeriodicWave::maxNumberOfPartials() const
   144 {
   145     return m_periodicWaveSize / 2;
   146 }
   148 unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const
   149 {
   150     // Number of cents below nyquist where we cull partials.
   151     float centsToCull = rangeIndex * m_centsPerRange;
   153     // A value from 0 -> 1 representing what fraction of the partials to keep.
   154     float cullingScale = pow(2, -centsToCull / 1200);
   156     // The very top range will have all the partials culled.
   157     unsigned numberOfPartials = cullingScale * maxNumberOfPartials();
   159     return numberOfPartials;
   160 }
   162 // Convert into time-domain wave buffers.
   163 // One table is created for each range for non-aliasing playback
   164 // at different playback rates. Thus, higher ranges have more
   165 // high-frequency partials culled out.
   166 void PeriodicWave::createBandLimitedTables(const float* realData, const float* imagData, unsigned numberOfComponents)
   167 {
   168     float normalizationScale = 1;
   170     unsigned fftSize = m_periodicWaveSize;
   171     unsigned halfSize = fftSize / 2 + 1;
   172     unsigned i;
   174     numberOfComponents = std::min(numberOfComponents, halfSize);
   176     m_bandLimitedTables.SetCapacity(m_numberOfRanges);
   178     for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) {
   179         // This FFTBlock is used to cull partials (represented by frequency bins).
   180         FFTBlock frame(fftSize);
   181         nsAutoArrayPtr<float> realP(new float[halfSize]);
   182         nsAutoArrayPtr<float> imagP(new float[halfSize]);
   184         // Copy from loaded frequency data and scale.
   185         float scale = fftSize;
   186         AudioBufferCopyWithScale(realData, scale, realP, numberOfComponents);
   187         AudioBufferCopyWithScale(imagData, scale, imagP, numberOfComponents);
   189         // If fewer components were provided than 1/2 FFT size,
   190         // then clear the remaining bins.
   191         for (i = numberOfComponents; i < halfSize; ++i) {
   192             realP[i] = 0;
   193             imagP[i] = 0;
   194         }
   196         // Generate complex conjugate because of the way the
   197         // inverse FFT is defined.
   198         float minusOne = -1;
   199         AudioBufferInPlaceScale(imagP, minusOne, halfSize);
   201         // Find the starting bin where we should start culling.
   202         // We need to clear out the highest frequencies to band-limit
   203         // the waveform.
   204         unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex);
   206         // Cull the aliasing partials for this pitch range.
   207         for (i = numberOfPartials + 1; i < halfSize; ++i) {
   208             realP[i] = 0;
   209             imagP[i] = 0;
   210         }
   211         // Clear nyquist if necessary.
   212         if (numberOfPartials < halfSize)
   213             realP[halfSize-1] = 0;
   215         // Clear any DC-offset.
   216         realP[0] = 0;
   218         // Clear values which have no effect.
   219         imagP[0] = 0;
   220         imagP[halfSize-1] = 0;
   222         // Create the band-limited table.
   223         AudioFloatArray* table = new AudioFloatArray(m_periodicWaveSize);
   224         m_bandLimitedTables.AppendElement(table);
   226         // Apply an inverse FFT to generate the time-domain table data.
   227         float* data = m_bandLimitedTables[rangeIndex]->Elements();
   228         frame.PerformInverseFFT(realP, imagP, data);
   230         // For the first range (which has the highest power), calculate
   231         // its peak value then compute normalization scale.
   232         if (!rangeIndex) {
   233             float maxValue;
   234             maxValue = AudioBufferPeakValue(data, m_periodicWaveSize);
   236             if (maxValue)
   237                 normalizationScale = 1.0f / maxValue;
   238         }
   240         // Apply normalization scale.
   241         AudioBufferInPlaceScale(data, normalizationScale, m_periodicWaveSize);
   242     }
   243 }
   245 void PeriodicWave::generateBasicWaveform(OscillatorType shape)
   246 {
   247     const float piFloat = M_PI;
   248     unsigned fftSize = periodicWaveSize();
   249     unsigned halfSize = fftSize / 2 + 1;
   251     AudioFloatArray real(halfSize);
   252     AudioFloatArray imag(halfSize);
   253     float* realP = real.Elements();
   254     float* imagP = imag.Elements();
   256     // Clear DC and Nyquist.
   257     realP[0] = 0;
   258     imagP[0] = 0;
   259     realP[halfSize-1] = 0;
   260     imagP[halfSize-1] = 0;
   262     for (unsigned n = 1; n < halfSize; ++n) {
   263         float omega = 2 * piFloat * n;
   264         float invOmega = 1 / omega;
   266         // Fourier coefficients according to standard definition.
   267         float a; // Coefficient for cos().
   268         float b; // Coefficient for sin().
   270         // Calculate Fourier coefficients depending on the shape.
   271         // Note that the overall scaling (magnitude) of the waveforms
   272         // is normalized in createBandLimitedTables().
   273         switch (shape) {
   274         case OscillatorType::Sine:
   275             // Standard sine wave function.
   276             a = 0;
   277             b = (n == 1) ? 1 : 0;
   278             break;
   279         case OscillatorType::Square:
   280             // Square-shaped waveform with the first half its maximum value
   281             // and the second half its minimum value.
   282             a = 0;
   283             b = invOmega * ((n & 1) ? 2 : 0);
   284             break;
   285         case OscillatorType::Sawtooth:
   286             // Sawtooth-shaped waveform with the first half ramping from
   287             // zero to maximum and the second half from minimum to zero.
   288             a = 0;
   289             b = -invOmega * cos(0.5 * omega);
   290             break;
   291         case OscillatorType::Triangle:
   292             // Triangle-shaped waveform going from its maximum value to
   293             // its minimum value then back to the maximum value.
   294             a = (4 - 4 * cos(0.5 * omega)) / (n * n * piFloat * piFloat);
   295             b = 0;
   296             break;
   297         default:
   298             NS_NOTREACHED("invalid oscillator type");
   299             a = 0;
   300             b = 0;
   301             break;
   302         }
   304         realP[n] = a;
   305         imagP[n] = b;
   306     }
   308     createBandLimitedTables(realP, imagP, halfSize);
   309 }
   311 } // namespace WebCore

mercurial