tor-browser

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

AudioVerifier.h (4727B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef DOM_MEDIA_GTEST_AUDIOVERIFIER_H_
      8 #define DOM_MEDIA_GTEST_AUDIOVERIFIER_H_
      9 
     10 #include "AudioGenerator.h"
     11 
     12 namespace mozilla {
     13 
     14 template <typename Sample>
     15 class AudioVerifier {
     16 public:
     17  explicit AudioVerifier(uint32_t aRate, uint32_t aFrequency)
     18      : mRate(aRate), mFrequency(aFrequency) {}
     19 
     20  // Only the mono channel is taken into account.
     21  void AppendData(const AudioSegment& segment) {
     22    for (AudioSegment::ConstChunkIterator iter(segment); !iter.IsEnded();
     23         iter.Next()) {
     24      const AudioChunk& c = *iter;
     25      if (c.IsNull()) {
     26        for (int i = 0; i < c.GetDuration(); ++i) {
     27          CheckSample(0);
     28        }
     29      } else {
     30        const Sample* buffer = c.ChannelData<Sample>()[0];
     31        for (int i = 0; i < c.GetDuration(); ++i) {
     32          CheckSample(buffer[i]);
     33        }
     34      }
     35    }
     36  }
     37 
     38  void AppendDataInterleaved(const Sample* aBuffer, uint32_t aFrames,
     39                             uint32_t aChannels) {
     40    for (uint32_t i = 0; i < aFrames * aChannels; i += aChannels) {
     41      CheckSample(aBuffer[i]);
     42    }
     43  }
     44 
     45  float EstimatedFreq() const {
     46    if (mTotalFramesSoFar == PreSilenceSamples()) {
     47      return 0;
     48    }
     49    if (mSumPeriodInSamples == 0) {
     50      return 0;
     51    }
     52    if (mZeroCrossCount <= 1) {
     53      return 0;
     54    }
     55    return mRate /
     56           (static_cast<float>(mSumPeriodInSamples) / (mZeroCrossCount - 1));
     57  }
     58 
     59  // Returns the maximum difference in value between two adjacent samples along
     60  // the sine curve.
     61  Sample MaxMagnitudeDifference() {
     62    return static_cast<Sample>(AudioGenerator<Sample>::Amplitude() * 2 *
     63                               sin(2 * M_PI * mFrequency / mRate));
     64  }
     65 
     66  bool PreSilenceEnded() const {
     67    return mTotalFramesSoFar > mPreSilenceSamples;
     68  }
     69  uint64_t PreSilenceSamples() const { return mPreSilenceSamples; }
     70  uint32_t CountDiscontinuities() const { return mDiscontinuitiesCount; }
     71 
     72 private:
     73  void CheckSample(Sample aCurrentSample) {
     74    ++mTotalFramesSoFar;
     75    // Avoid pre-silence
     76    if (!CountPreSilence(aCurrentSample)) {
     77      CountZeroCrossing(aCurrentSample);
     78      CountDiscontinuities(aCurrentSample);
     79    }
     80 
     81    mPrevious = aCurrentSample;
     82  }
     83 
     84  bool CountPreSilence(Sample aCurrentSample) {
     85    if (IsZero(aCurrentSample) && mPreSilenceSamples == mTotalFramesSoFar - 1) {
     86      ++mPreSilenceSamples;
     87      return true;
     88    }
     89    if (IsZero(mPrevious) && aCurrentSample > 0 &&
     90        aCurrentSample < 2 * MaxMagnitudeDifference() &&
     91        mPreSilenceSamples == mTotalFramesSoFar - 1) {
     92      // Previous zero considered the first sample of the waveform.
     93      --mPreSilenceSamples;
     94    }
     95    return false;
     96  }
     97 
     98  // Positive to negative direction
     99  void CountZeroCrossing(Sample aCurrentSample) {
    100    if (mPrevious > 0 && aCurrentSample <= 0) {
    101      if (mZeroCrossCount++) {
    102        MOZ_RELEASE_ASSERT(mZeroCrossCount > 1);
    103        mSumPeriodInSamples += mTotalFramesSoFar - mLastZeroCrossPosition;
    104      }
    105      mLastZeroCrossPosition = mTotalFramesSoFar;
    106    }
    107  }
    108 
    109  void CountDiscontinuities(Sample aCurrentSample) {
    110    // The factor of 2 tolerates up to 1 skipped frame.
    111    const bool haveDiscontinuity =
    112        fabs(aCurrentSample - mPrevious) > 2 * MaxMagnitudeDifference();
    113 
    114    if (mCurrentDiscontinuityFrameCount > 0) {
    115      if (++mCurrentDiscontinuityFrameCount == 5) {
    116        // Allow a grace-period of 5 samples for any given discontinuity.
    117        // For instance the speex resampler can smooth out a sudden drop to 0
    118        // over several samples.
    119        mCurrentDiscontinuityFrameCount = 0;
    120      }
    121      return;
    122    }
    123 
    124    MOZ_RELEASE_ASSERT(mCurrentDiscontinuityFrameCount == 0);
    125    if (!haveDiscontinuity) {
    126      return;
    127    }
    128 
    129    // Encountered a new discontinuity.
    130    ++mCurrentDiscontinuityFrameCount;
    131    ++mDiscontinuitiesCount;
    132  }
    133 
    134  bool IsZero(float aValue) { return fabs(aValue) < 1e-8; }
    135  bool IsZero(short aValue) { return aValue == 0; }
    136 
    137 private:
    138  const uint32_t mRate;
    139  const uint32_t mFrequency;
    140 
    141  uint32_t mZeroCrossCount = 0;
    142  uint64_t mLastZeroCrossPosition = 0;
    143  uint64_t mSumPeriodInSamples = 0;
    144 
    145  uint64_t mTotalFramesSoFar = 0;
    146  uint64_t mPreSilenceSamples = 0;
    147 
    148  uint32_t mCurrentDiscontinuityFrameCount = 0;
    149  uint32_t mDiscontinuitiesCount = 0;
    150  // This is needed to connect the previous buffers.
    151  Sample mPrevious = {};
    152 };
    153 
    154 }  // namespace mozilla
    155 
    156 #endif  // DOM_MEDIA_GTEST_AUDIOVERIFIER_H_