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_