tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PeriodicWave.cpp (13670B)


      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 */
     28 
     29 #include "PeriodicWave.h"
     30 
     31 #include <algorithm>
     32 #include <cmath>
     33 
     34 #include "mozilla/FFTBlock.h"
     35 
     36 const unsigned MinPeriodicWaveSize = 4096;  // This must be a power of two.
     37 const unsigned MaxPeriodicWaveSize = 8192;  // This must be a power of two.
     38 const float CentsPerRange = 1200 / 3;       // 1/3 Octave.
     39 
     40 using namespace mozilla;
     41 using mozilla::dom::OscillatorType;
     42 
     43 namespace WebCore {
     44 
     45 already_AddRefed<PeriodicWave> PeriodicWave::create(float sampleRate,
     46                                                    const float* real,
     47                                                    const float* imag,
     48                                                    size_t numberOfComponents,
     49                                                    bool disableNormalization) {
     50  bool isGood = real && imag && numberOfComponents > 0;
     51  MOZ_ASSERT(isGood);
     52  if (isGood) {
     53    RefPtr<PeriodicWave> periodicWave =
     54        new PeriodicWave(sampleRate, numberOfComponents, disableNormalization);
     55 
     56    // Limit the number of components used to those for frequencies below the
     57    // Nyquist of the fixed length inverse FFT.
     58    size_t halfSize = periodicWave->m_periodicWaveSize / 2;
     59    numberOfComponents = std::min(numberOfComponents, halfSize);
     60    periodicWave->m_numberOfComponents = numberOfComponents;
     61    periodicWave->m_realComponents =
     62        MakeUnique<AudioFloatArray>(numberOfComponents);
     63    periodicWave->m_imagComponents =
     64        MakeUnique<AudioFloatArray>(numberOfComponents);
     65    memcpy(periodicWave->m_realComponents->Elements(), real,
     66           numberOfComponents * sizeof(float));
     67    memcpy(periodicWave->m_imagComponents->Elements(), imag,
     68           numberOfComponents * sizeof(float));
     69 
     70    return periodicWave.forget();
     71  }
     72  return nullptr;
     73 }
     74 
     75 already_AddRefed<PeriodicWave> PeriodicWave::createSine(float sampleRate) {
     76  RefPtr<PeriodicWave> periodicWave =
     77      new PeriodicWave(sampleRate, MinPeriodicWaveSize, false);
     78  periodicWave->generateBasicWaveform(OscillatorType::Sine);
     79  return periodicWave.forget();
     80 }
     81 
     82 already_AddRefed<PeriodicWave> PeriodicWave::createSquare(float sampleRate) {
     83  RefPtr<PeriodicWave> periodicWave =
     84      new PeriodicWave(sampleRate, MinPeriodicWaveSize, false);
     85  periodicWave->generateBasicWaveform(OscillatorType::Square);
     86  return periodicWave.forget();
     87 }
     88 
     89 already_AddRefed<PeriodicWave> PeriodicWave::createSawtooth(float sampleRate) {
     90  RefPtr<PeriodicWave> periodicWave =
     91      new PeriodicWave(sampleRate, MinPeriodicWaveSize, false);
     92  periodicWave->generateBasicWaveform(OscillatorType::Sawtooth);
     93  return periodicWave.forget();
     94 }
     95 
     96 already_AddRefed<PeriodicWave> PeriodicWave::createTriangle(float sampleRate) {
     97  RefPtr<PeriodicWave> periodicWave =
     98      new PeriodicWave(sampleRate, MinPeriodicWaveSize, false);
     99  periodicWave->generateBasicWaveform(OscillatorType::Triangle);
    100  return periodicWave.forget();
    101 }
    102 
    103 PeriodicWave::PeriodicWave(float sampleRate, size_t numberOfComponents,
    104                           bool disableNormalization)
    105    : m_sampleRate(sampleRate),
    106      m_centsPerRange(CentsPerRange),
    107      m_maxPartialsInBandLimitedTable(0),
    108      m_normalizationScale(1.0f),
    109      m_disableNormalization(disableNormalization) {
    110  float nyquist = 0.5 * m_sampleRate;
    111 
    112  if (numberOfComponents <= MinPeriodicWaveSize) {
    113    m_periodicWaveSize = MinPeriodicWaveSize;
    114  } else {
    115    unsigned npow2 = fdlibm_exp2f(floorf(
    116        fdlibm_logf(numberOfComponents - 1.0) / fdlibm_logf(2.0f) + 1.0f));
    117    m_periodicWaveSize = std::min(MaxPeriodicWaveSize, npow2);
    118  }
    119 
    120  m_numberOfRanges =
    121      (unsigned)(3.0f * fdlibm_logf(m_periodicWaveSize) / fdlibm_logf(2.0f));
    122  m_bandLimitedTables.SetLength(m_numberOfRanges);
    123  m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
    124  m_rateScale = m_periodicWaveSize / m_sampleRate;
    125 }
    126 
    127 size_t PeriodicWave::sizeOfIncludingThis(
    128    mozilla::MallocSizeOf aMallocSizeOf) const {
    129  size_t amount = aMallocSizeOf(this);
    130 
    131  amount += m_bandLimitedTables.ShallowSizeOfExcludingThis(aMallocSizeOf);
    132  for (size_t i = 0; i < m_bandLimitedTables.Length(); i++) {
    133    if (m_bandLimitedTables[i]) {
    134      amount +=
    135          m_bandLimitedTables[i]->ShallowSizeOfIncludingThis(aMallocSizeOf);
    136    }
    137  }
    138 
    139  return amount;
    140 }
    141 
    142 void PeriodicWave::waveDataForFundamentalFrequency(
    143    float fundamentalFrequency, float*& lowerWaveData, float*& higherWaveData,
    144    float& tableInterpolationFactor) {
    145  // Negative frequencies are allowed, in which case we alias
    146  // to the positive frequency.
    147  fundamentalFrequency = fabsf(fundamentalFrequency);
    148 
    149  // We only need to rebuild to the tables if the new fundamental
    150  // frequency is low enough to allow for more partials below the
    151  // Nyquist frequency.
    152  unsigned numberOfPartials = numberOfPartialsForRange(0);
    153  float nyquist = 0.5 * m_sampleRate;
    154  if (fundamentalFrequency != 0.0) {
    155    numberOfPartials =
    156        std::min(numberOfPartials, (unsigned)(nyquist / fundamentalFrequency));
    157  }
    158  if (numberOfPartials > m_maxPartialsInBandLimitedTable) {
    159    for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) {
    160      m_bandLimitedTables[rangeIndex] = 0;
    161    }
    162 
    163    // We need to create the first table to determine the normalization
    164    // constant.
    165    createBandLimitedTables(fundamentalFrequency, 0);
    166    m_maxPartialsInBandLimitedTable = numberOfPartials;
    167  }
    168 
    169  // Calculate the pitch range.
    170  float ratio = fundamentalFrequency > 0
    171                    ? fundamentalFrequency / m_lowestFundamentalFrequency
    172                    : 0.5;
    173  float centsAboveLowestFrequency =
    174      fdlibm_logf(ratio) / fdlibm_logf(2.0f) * 1200;
    175 
    176  // Add one to round-up to the next range just in time to truncate
    177  // partials before aliasing occurs.
    178  float pitchRange = 1 + centsAboveLowestFrequency / m_centsPerRange;
    179 
    180  pitchRange = std::max(pitchRange, 0.0f);
    181  pitchRange = std::min(pitchRange, static_cast<float>(m_numberOfRanges - 1));
    182 
    183  // The words "lower" and "higher" refer to the table data having
    184  // the lower and higher numbers of partials. It's a little confusing
    185  // since the range index gets larger the more partials we cull out.
    186  // So the lower table data will have a larger range index.
    187  unsigned rangeIndex1 = static_cast<unsigned>(pitchRange);
    188  unsigned rangeIndex2 =
    189      rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1;
    190 
    191  if (!m_bandLimitedTables[rangeIndex1].get())
    192    createBandLimitedTables(fundamentalFrequency, rangeIndex1);
    193 
    194  if (!m_bandLimitedTables[rangeIndex2].get())
    195    createBandLimitedTables(fundamentalFrequency, rangeIndex2);
    196 
    197  lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements();
    198  higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements();
    199 
    200  // Ranges from 0 -> 1 to interpolate between lower -> higher.
    201  tableInterpolationFactor = rangeIndex2 - pitchRange;
    202 }
    203 
    204 unsigned PeriodicWave::maxNumberOfPartials() const {
    205  return m_periodicWaveSize / 2;
    206 }
    207 
    208 unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const {
    209  // Number of cents below nyquist where we cull partials.
    210  float centsToCull = rangeIndex * m_centsPerRange;
    211 
    212  // A value from 0 -> 1 representing what fraction of the partials to keep.
    213  float cullingScale = fdlibm_exp2f(-centsToCull / 1200);
    214 
    215  // The very top range will have all the partials culled.
    216  unsigned numberOfPartials = cullingScale * maxNumberOfPartials();
    217 
    218  return numberOfPartials;
    219 }
    220 
    221 // Convert into time-domain wave buffers.
    222 // One table is created for each range for non-aliasing playback
    223 // at different playback rates. Thus, higher ranges have more
    224 // high-frequency partials culled out.
    225 void PeriodicWave::createBandLimitedTables(float fundamentalFrequency,
    226                                           unsigned rangeIndex) {
    227  unsigned fftSize = m_periodicWaveSize;
    228  unsigned i;
    229 
    230  const float* realData = m_realComponents->Elements();
    231  const float* imagData = m_imagComponents->Elements();
    232 
    233  // This FFTBlock is used to cull partials (represented by frequency bins).
    234  FFTBlock frame(fftSize);
    235 
    236  // Find the starting bin where we should start culling the aliasing
    237  // partials for this pitch range.  We need to clear out the highest
    238  // frequencies to band-limit the waveform.
    239  unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex);
    240  // Also limit to the number of components that are provided.
    241  numberOfPartials = std::min(numberOfPartials, m_numberOfComponents - 1);
    242 
    243  // Limit number of partials to those below Nyquist frequency
    244  float nyquist = 0.5 * m_sampleRate;
    245  if (fundamentalFrequency != 0.0) {
    246    numberOfPartials =
    247        std::min(numberOfPartials, (unsigned)(nyquist / fundamentalFrequency));
    248  }
    249 
    250  // Copy from loaded frequency data and generate complex conjugate
    251  // because of the way the inverse FFT is defined.
    252  // The coefficients of higher partials remain zero, as initialized in
    253  // the FFTBlock constructor.
    254  for (i = 0; i < numberOfPartials + 1; ++i) {
    255    frame.RealData(i) = realData[i];
    256    frame.ImagData(i) = -imagData[i];
    257  }
    258 
    259  // Clear any DC-offset.
    260  frame.RealData(0) = 0;
    261  // Clear value which has no effect.
    262  frame.ImagData(0) = 0;
    263 
    264  // Create the band-limited table.
    265  m_bandLimitedTables[rangeIndex] =
    266      MakeUnique<AlignedAudioFloatArray>(m_periodicWaveSize);
    267 
    268  // Apply an inverse FFT to generate the time-domain table data.
    269  float* data = m_bandLimitedTables[rangeIndex]->Elements();
    270  frame.GetInverse(data);
    271 
    272  // For the first range (which has the highest power), calculate
    273  // its peak value then compute normalization scale.
    274  if (m_disableNormalization) {
    275    // See Bug 1424906, results need to be scaled by 0.5 even
    276    // when normalization is disabled.
    277    m_normalizationScale = 0.5;
    278  } else if (!rangeIndex) {
    279    float maxValue;
    280    maxValue = AudioBufferPeakValue(data, m_periodicWaveSize);
    281 
    282    if (maxValue) m_normalizationScale = 1.0f / maxValue;
    283  }
    284 
    285  // Apply normalization scale.
    286  AudioBufferInPlaceScale(data, m_normalizationScale, m_periodicWaveSize);
    287 }
    288 
    289 void PeriodicWave::generateBasicWaveform(OscillatorType shape) {
    290  const float piFloat = float(M_PI);
    291  unsigned fftSize = periodicWaveSize();
    292  unsigned halfSize = fftSize / 2;
    293 
    294  m_numberOfComponents = halfSize;
    295  m_realComponents = MakeUnique<AudioFloatArray>(halfSize);
    296  m_imagComponents = MakeUnique<AudioFloatArray>(halfSize);
    297  float* realP = m_realComponents->Elements();
    298  float* imagP = m_imagComponents->Elements();
    299 
    300  // Clear DC and imag value which is ignored.
    301  realP[0] = 0;
    302  imagP[0] = 0;
    303 
    304  for (unsigned n = 1; n < halfSize; ++n) {
    305    float omega = 2 * piFloat * n;
    306    float invOmega = 1 / omega;
    307 
    308    // Fourier coefficients according to standard definition.
    309    float a;  // Coefficient for cos().
    310    float b;  // Coefficient for sin().
    311 
    312    // Calculate Fourier coefficients depending on the shape.
    313    // Note that the overall scaling (magnitude) of the waveforms
    314    // is normalized in createBandLimitedTables().
    315    switch (shape) {
    316      case OscillatorType::Sine:
    317        // Standard sine wave function.
    318        a = 0;
    319        b = (n == 1) ? 1 : 0;
    320        break;
    321      case OscillatorType::Square:
    322        // Square-shaped waveform with the first half its maximum value
    323        // and the second half its minimum value.
    324        a = 0;
    325        b = invOmega * ((n & 1) ? 2 : 0);
    326        break;
    327      case OscillatorType::Sawtooth:
    328        // Sawtooth-shaped waveform with the first half ramping from
    329        // zero to maximum and the second half from minimum to zero.
    330        a = 0;
    331        b = -invOmega * fdlibm_cos(0.5 * omega);
    332        break;
    333      case OscillatorType::Triangle:
    334        // Triangle-shaped waveform going from its maximum value to
    335        // its minimum value then back to the maximum value.
    336        a = 0;
    337        if (n & 1) {
    338          b = 2 * (2 / (n * piFloat) * 2 / (n * piFloat)) *
    339              ((((n - 1) >> 1) & 1) ? -1 : 1);
    340        } else {
    341          b = 0;
    342        }
    343        break;
    344      default:
    345        MOZ_ASSERT_UNREACHABLE("invalid oscillator type");
    346        a = 0;
    347        b = 0;
    348        break;
    349    }
    350 
    351    realP[n] = a;
    352    imagP[n] = b;
    353  }
    354 }
    355 
    356 }  // namespace WebCore