content/media/webaudio/blink/PeriodicWave.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/webaudio/blink/PeriodicWave.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,311 @@
     1.4 +/*
     1.5 + * Copyright (C) 2012 Google Inc. All rights reserved.
     1.6 + *
     1.7 + * Redistribution and use in source and binary forms, with or without
     1.8 + * modification, are permitted provided that the following conditions
     1.9 + * are met:
    1.10 + *
    1.11 + * 1.  Redistributions of source code must retain the above copyright
    1.12 + *     notice, this list of conditions and the following disclaimer.
    1.13 + * 2.  Redistributions in binary form must reproduce the above copyright
    1.14 + *     notice, this list of conditions and the following disclaimer in the
    1.15 + *     documentation and/or other materials provided with the distribution.
    1.16 + * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
    1.17 + *     its contributors may be used to endorse or promote products derived
    1.18 + *     from this software without specific prior written permission.
    1.19 + *
    1.20 + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
    1.21 + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    1.22 + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    1.23 + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
    1.24 + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    1.25 + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    1.26 + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    1.27 + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    1.28 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    1.29 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.30 + */
    1.31 +
    1.32 +#include "PeriodicWave.h"
    1.33 +#include <algorithm>
    1.34 +#include <cmath>
    1.35 +#include "mozilla/FFTBlock.h"
    1.36 +
    1.37 +const unsigned PeriodicWaveSize = 4096; // This must be a power of two.
    1.38 +const unsigned NumberOfRanges = 36; // There should be 3 * log2(PeriodicWaveSize) 1/3 octave ranges.
    1.39 +const float CentsPerRange = 1200 / 3; // 1/3 Octave.
    1.40 +
    1.41 +using namespace mozilla;
    1.42 +using mozilla::dom::OscillatorType;
    1.43 +
    1.44 +namespace WebCore {
    1.45 +
    1.46 +PeriodicWave* PeriodicWave::create(float sampleRate,
    1.47 +                                   const float* real,
    1.48 +                                   const float* imag,
    1.49 +                                   size_t numberOfComponents)
    1.50 +{
    1.51 +    bool isGood = real && imag && numberOfComponents > 0 &&
    1.52 +         numberOfComponents <= PeriodicWaveSize;
    1.53 +    MOZ_ASSERT(isGood);
    1.54 +    if (isGood) {
    1.55 +        PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
    1.56 +        periodicWave->createBandLimitedTables(real, imag, numberOfComponents);
    1.57 +        return periodicWave;
    1.58 +    }
    1.59 +    return 0;
    1.60 +}
    1.61 +
    1.62 +PeriodicWave* PeriodicWave::createSine(float sampleRate)
    1.63 +{
    1.64 +      PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
    1.65 +          periodicWave->generateBasicWaveform(OscillatorType::Sine);
    1.66 +              return periodicWave;
    1.67 +}
    1.68 +
    1.69 +PeriodicWave* PeriodicWave::createSquare(float sampleRate)
    1.70 +{
    1.71 +      PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
    1.72 +          periodicWave->generateBasicWaveform(OscillatorType::Square);
    1.73 +              return periodicWave;
    1.74 +}
    1.75 +
    1.76 +PeriodicWave* PeriodicWave::createSawtooth(float sampleRate)
    1.77 +{
    1.78 +      PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
    1.79 +          periodicWave->generateBasicWaveform(OscillatorType::Sawtooth);
    1.80 +              return periodicWave;
    1.81 +}
    1.82 +
    1.83 +PeriodicWave* PeriodicWave::createTriangle(float sampleRate)
    1.84 +{
    1.85 +      PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
    1.86 +          periodicWave->generateBasicWaveform(OscillatorType::Triangle);
    1.87 +              return periodicWave;
    1.88 +}
    1.89 +
    1.90 +PeriodicWave::PeriodicWave(float sampleRate)
    1.91 +    : m_sampleRate(sampleRate)
    1.92 +    , m_periodicWaveSize(PeriodicWaveSize)
    1.93 +    , m_numberOfRanges(NumberOfRanges)
    1.94 +    , m_centsPerRange(CentsPerRange)
    1.95 +{
    1.96 +    float nyquist = 0.5 * m_sampleRate;
    1.97 +    m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
    1.98 +    m_rateScale = m_periodicWaveSize / m_sampleRate;
    1.99 +}
   1.100 +
   1.101 +size_t PeriodicWave::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
   1.102 +{
   1.103 +    size_t amount = aMallocSizeOf(this);
   1.104 +
   1.105 +    amount += m_bandLimitedTables.SizeOfExcludingThis(aMallocSizeOf);
   1.106 +    for (size_t i = 0; i < m_bandLimitedTables.Length(); i++) {
   1.107 +        if (m_bandLimitedTables[i]) {
   1.108 +            amount += m_bandLimitedTables[i]->SizeOfIncludingThis(aMallocSizeOf);
   1.109 +        }
   1.110 +    }
   1.111 +
   1.112 +    return amount;
   1.113 +}
   1.114 +
   1.115 +void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor)
   1.116 +{
   1.117 +    // Negative frequencies are allowed, in which case we alias
   1.118 +    // to the positive frequency.
   1.119 +    fundamentalFrequency = fabsf(fundamentalFrequency);
   1.120 +
   1.121 +    // Calculate the pitch range.
   1.122 +    float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5;
   1.123 +    float centsAboveLowestFrequency = logf(ratio)/logf(2.0f) * 1200;
   1.124 +
   1.125 +    // Add one to round-up to the next range just in time to truncate
   1.126 +    // partials before aliasing occurs.
   1.127 +    float pitchRange = 1 + centsAboveLowestFrequency / m_centsPerRange;
   1.128 +
   1.129 +    pitchRange = std::max(pitchRange, 0.0f);
   1.130 +    pitchRange = std::min(pitchRange, static_cast<float>(m_numberOfRanges - 1));
   1.131 +
   1.132 +    // The words "lower" and "higher" refer to the table data having
   1.133 +    // the lower and higher numbers of partials. It's a little confusing
   1.134 +    // since the range index gets larger the more partials we cull out.
   1.135 +    // So the lower table data will have a larger range index.
   1.136 +    unsigned rangeIndex1 = static_cast<unsigned>(pitchRange);
   1.137 +    unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1;
   1.138 +
   1.139 +    lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements();
   1.140 +    higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements();
   1.141 +
   1.142 +    // Ranges from 0 -> 1 to interpolate between lower -> higher.
   1.143 +    tableInterpolationFactor = pitchRange - rangeIndex1;
   1.144 +}
   1.145 +
   1.146 +unsigned PeriodicWave::maxNumberOfPartials() const
   1.147 +{
   1.148 +    return m_periodicWaveSize / 2;
   1.149 +}
   1.150 +
   1.151 +unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const
   1.152 +{
   1.153 +    // Number of cents below nyquist where we cull partials.
   1.154 +    float centsToCull = rangeIndex * m_centsPerRange;
   1.155 +
   1.156 +    // A value from 0 -> 1 representing what fraction of the partials to keep.
   1.157 +    float cullingScale = pow(2, -centsToCull / 1200);
   1.158 +
   1.159 +    // The very top range will have all the partials culled.
   1.160 +    unsigned numberOfPartials = cullingScale * maxNumberOfPartials();
   1.161 +
   1.162 +    return numberOfPartials;
   1.163 +}
   1.164 +
   1.165 +// Convert into time-domain wave buffers.
   1.166 +// One table is created for each range for non-aliasing playback
   1.167 +// at different playback rates. Thus, higher ranges have more
   1.168 +// high-frequency partials culled out.
   1.169 +void PeriodicWave::createBandLimitedTables(const float* realData, const float* imagData, unsigned numberOfComponents)
   1.170 +{
   1.171 +    float normalizationScale = 1;
   1.172 +
   1.173 +    unsigned fftSize = m_periodicWaveSize;
   1.174 +    unsigned halfSize = fftSize / 2 + 1;
   1.175 +    unsigned i;
   1.176 +
   1.177 +    numberOfComponents = std::min(numberOfComponents, halfSize);
   1.178 +
   1.179 +    m_bandLimitedTables.SetCapacity(m_numberOfRanges);
   1.180 +
   1.181 +    for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) {
   1.182 +        // This FFTBlock is used to cull partials (represented by frequency bins).
   1.183 +        FFTBlock frame(fftSize);
   1.184 +        nsAutoArrayPtr<float> realP(new float[halfSize]);
   1.185 +        nsAutoArrayPtr<float> imagP(new float[halfSize]);
   1.186 +
   1.187 +        // Copy from loaded frequency data and scale.
   1.188 +        float scale = fftSize;
   1.189 +        AudioBufferCopyWithScale(realData, scale, realP, numberOfComponents);
   1.190 +        AudioBufferCopyWithScale(imagData, scale, imagP, numberOfComponents);
   1.191 +
   1.192 +        // If fewer components were provided than 1/2 FFT size,
   1.193 +        // then clear the remaining bins.
   1.194 +        for (i = numberOfComponents; i < halfSize; ++i) {
   1.195 +            realP[i] = 0;
   1.196 +            imagP[i] = 0;
   1.197 +        }
   1.198 +
   1.199 +        // Generate complex conjugate because of the way the
   1.200 +        // inverse FFT is defined.
   1.201 +        float minusOne = -1;
   1.202 +        AudioBufferInPlaceScale(imagP, minusOne, halfSize);
   1.203 +
   1.204 +        // Find the starting bin where we should start culling.
   1.205 +        // We need to clear out the highest frequencies to band-limit
   1.206 +        // the waveform.
   1.207 +        unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex);
   1.208 +
   1.209 +        // Cull the aliasing partials for this pitch range.
   1.210 +        for (i = numberOfPartials + 1; i < halfSize; ++i) {
   1.211 +            realP[i] = 0;
   1.212 +            imagP[i] = 0;
   1.213 +        }
   1.214 +        // Clear nyquist if necessary.
   1.215 +        if (numberOfPartials < halfSize)
   1.216 +            realP[halfSize-1] = 0;
   1.217 +
   1.218 +        // Clear any DC-offset.
   1.219 +        realP[0] = 0;
   1.220 +
   1.221 +        // Clear values which have no effect.
   1.222 +        imagP[0] = 0;
   1.223 +        imagP[halfSize-1] = 0;
   1.224 +
   1.225 +        // Create the band-limited table.
   1.226 +        AudioFloatArray* table = new AudioFloatArray(m_periodicWaveSize);
   1.227 +        m_bandLimitedTables.AppendElement(table);
   1.228 +
   1.229 +        // Apply an inverse FFT to generate the time-domain table data.
   1.230 +        float* data = m_bandLimitedTables[rangeIndex]->Elements();
   1.231 +        frame.PerformInverseFFT(realP, imagP, data);
   1.232 +
   1.233 +        // For the first range (which has the highest power), calculate
   1.234 +        // its peak value then compute normalization scale.
   1.235 +        if (!rangeIndex) {
   1.236 +            float maxValue;
   1.237 +            maxValue = AudioBufferPeakValue(data, m_periodicWaveSize);
   1.238 +
   1.239 +            if (maxValue)
   1.240 +                normalizationScale = 1.0f / maxValue;
   1.241 +        }
   1.242 +
   1.243 +        // Apply normalization scale.
   1.244 +        AudioBufferInPlaceScale(data, normalizationScale, m_periodicWaveSize);
   1.245 +    }
   1.246 +}
   1.247 +
   1.248 +void PeriodicWave::generateBasicWaveform(OscillatorType shape)
   1.249 +{
   1.250 +    const float piFloat = M_PI;
   1.251 +    unsigned fftSize = periodicWaveSize();
   1.252 +    unsigned halfSize = fftSize / 2 + 1;
   1.253 +
   1.254 +    AudioFloatArray real(halfSize);
   1.255 +    AudioFloatArray imag(halfSize);
   1.256 +    float* realP = real.Elements();
   1.257 +    float* imagP = imag.Elements();
   1.258 +
   1.259 +    // Clear DC and Nyquist.
   1.260 +    realP[0] = 0;
   1.261 +    imagP[0] = 0;
   1.262 +    realP[halfSize-1] = 0;
   1.263 +    imagP[halfSize-1] = 0;
   1.264 +
   1.265 +    for (unsigned n = 1; n < halfSize; ++n) {
   1.266 +        float omega = 2 * piFloat * n;
   1.267 +        float invOmega = 1 / omega;
   1.268 +
   1.269 +        // Fourier coefficients according to standard definition.
   1.270 +        float a; // Coefficient for cos().
   1.271 +        float b; // Coefficient for sin().
   1.272 +
   1.273 +        // Calculate Fourier coefficients depending on the shape.
   1.274 +        // Note that the overall scaling (magnitude) of the waveforms
   1.275 +        // is normalized in createBandLimitedTables().
   1.276 +        switch (shape) {
   1.277 +        case OscillatorType::Sine:
   1.278 +            // Standard sine wave function.
   1.279 +            a = 0;
   1.280 +            b = (n == 1) ? 1 : 0;
   1.281 +            break;
   1.282 +        case OscillatorType::Square:
   1.283 +            // Square-shaped waveform with the first half its maximum value
   1.284 +            // and the second half its minimum value.
   1.285 +            a = 0;
   1.286 +            b = invOmega * ((n & 1) ? 2 : 0);
   1.287 +            break;
   1.288 +        case OscillatorType::Sawtooth:
   1.289 +            // Sawtooth-shaped waveform with the first half ramping from
   1.290 +            // zero to maximum and the second half from minimum to zero.
   1.291 +            a = 0;
   1.292 +            b = -invOmega * cos(0.5 * omega);
   1.293 +            break;
   1.294 +        case OscillatorType::Triangle:
   1.295 +            // Triangle-shaped waveform going from its maximum value to
   1.296 +            // its minimum value then back to the maximum value.
   1.297 +            a = (4 - 4 * cos(0.5 * omega)) / (n * n * piFloat * piFloat);
   1.298 +            b = 0;
   1.299 +            break;
   1.300 +        default:
   1.301 +            NS_NOTREACHED("invalid oscillator type");
   1.302 +            a = 0;
   1.303 +            b = 0;
   1.304 +            break;
   1.305 +        }
   1.306 +
   1.307 +        realP[n] = a;
   1.308 +        imagP[n] = b;
   1.309 +    }
   1.310 +
   1.311 +    createBandLimitedTables(realP, imagP, halfSize);
   1.312 +}
   1.313 +
   1.314 +} // namespace WebCore

mercurial