• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ANALYZER_GLITCH_ANALYZER_H
18 #define ANALYZER_GLITCH_ANALYZER_H
19 
20 #include <algorithm>
21 #include <cctype>
22 #include <iomanip>
23 #include <iostream>
24 
25 #include "InfiniteRecording.h"
26 #include "LatencyAnalyzer.h"
27 #include "BaseSineAnalyzer.h"
28 #include "PseudoRandom.h"
29 
30 /**
31  * Output a steady sine wave and analyze the return signal.
32  *
33  * Use a cosine transform to measure the predicted magnitude and relative phase of the
34  * looped back sine wave. Then generate a predicted signal and compare with the actual signal.
35  */
36 class GlitchAnalyzer : public BaseSineAnalyzer {
37 public:
38 
GlitchAnalyzer()39     GlitchAnalyzer() : BaseSineAnalyzer() {}
40 
getState()41     int32_t getState() const {
42         return mState;
43     }
44 
getPeakAmplitude()45     double getPeakAmplitude() const {
46         return mPeakFollower.getLevel();
47     }
48 
getSineAmplitude()49     double getSineAmplitude() const {
50         return mMagnitude;
51     }
52 
getGlitchCount()53     int32_t getGlitchCount() const {
54         return mGlitchCount;
55     }
56 
getStateFrameCount(int state)57     int32_t getStateFrameCount(int state) const {
58         return mStateFrameCounters[state];
59     }
60 
getSignalToNoiseDB()61     double getSignalToNoiseDB() {
62         static const double threshold = 1.0e-14;
63         if (mState != STATE_LOCKED
64                 || mMeanSquareSignal < threshold
65                 || mMeanSquareNoise < threshold) {
66             return 0.0;
67         } else {
68             double signalToNoise = mMeanSquareSignal / mMeanSquareNoise; // power ratio
69             double signalToNoiseDB = 10.0 * log(signalToNoise);
70             if (signalToNoiseDB < MIN_SNR_DB) {
71                 setResult(ERROR_VOLUME_TOO_LOW);
72             }
73             return signalToNoiseDB;
74         }
75     }
76 
analyze()77     std::string analyze() override {
78         std::stringstream report;
79         report << "GlitchAnalyzer ------------------\n";
80         report << LOOPBACK_RESULT_TAG "peak.amplitude     = " << std::setw(8)
81                << getPeakAmplitude() << "\n";
82         report << LOOPBACK_RESULT_TAG "sine.magnitude     = " << std::setw(8)
83                << getSineAmplitude() << "\n";
84         report << LOOPBACK_RESULT_TAG "rms.noise          = " << std::setw(8)
85                << mMeanSquareNoise << "\n";
86         report << LOOPBACK_RESULT_TAG "signal.to.noise.db = " << std::setw(8)
87                << getSignalToNoiseDB() << "\n";
88         report << LOOPBACK_RESULT_TAG "frames.accumulated = " << std::setw(8)
89                << mFramesAccumulated << "\n";
90         report << LOOPBACK_RESULT_TAG "sine.period        = " << std::setw(8)
91                << mSinePeriod << "\n";
92         report << LOOPBACK_RESULT_TAG "test.state         = " << std::setw(8)
93                << mState << "\n";
94         report << LOOPBACK_RESULT_TAG "frame.count        = " << std::setw(8)
95                << mFrameCounter << "\n";
96         // Did we ever get a lock?
97         bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0);
98         if (!gotLock) {
99             report << "ERROR - failed to lock on reference sine tone.\n";
100             setResult(ERROR_NO_LOCK);
101         } else {
102             // Only print if meaningful.
103             report << LOOPBACK_RESULT_TAG "glitch.count       = " << std::setw(8)
104                    << mGlitchCount << "\n";
105             report << LOOPBACK_RESULT_TAG "max.glitch         = " << std::setw(8)
106                    << mMaxGlitchDelta << "\n";
107             if (mGlitchCount > 0) {
108                 report << "ERROR - number of glitches > 0\n";
109                 setResult(ERROR_GLITCHES);
110             }
111         }
112         return report.str();
113     }
114 
printStatus()115     void printStatus() override {
116         ALOGD("st = %d, #gl = %3d,", mState, mGlitchCount);
117     }
118 
119     /**
120      * @param frameData contains microphone data with sine signal feedback
121      * @param channelCount
122      */
processInputFrame(const float * frameData,int)123     result_code processInputFrame(const float *frameData, int /* channelCount */) override {
124         result_code result = RESULT_OK;
125 
126         float sample = frameData[getInputChannel()];
127         float peak = mPeakFollower.process(sample);
128         mInfiniteRecording.write(sample);
129 
130         // Force a periodic glitch to test the detector!
131         if (mForceGlitchDuration > 0) {
132             if (mForceGlitchCounter == 0) {
133                 ALOGE("%s: force a glitch!!", __func__);
134                 mForceGlitchCounter = getSampleRate();
135             } else if (mForceGlitchCounter <= mForceGlitchDuration) {
136                 // Force an abrupt offset.
137                 sample += (sample > 0.0) ? -0.5f : 0.5f;
138             }
139             --mForceGlitchCounter;
140         }
141 
142         mStateFrameCounters[mState]++; // count how many frames we are in each state
143 
144         switch (mState) {
145             case STATE_IDLE:
146                 mDownCounter--;
147                 if (mDownCounter <= 0) {
148                     mState = STATE_IMMUNE;
149                     mDownCounter = IMMUNE_FRAME_COUNT;
150                     mInputPhase = 0.0; // prevent spike at start
151                     mOutputPhase = 0.0;
152                     resetAccumulator();
153                 }
154                 break;
155 
156             case STATE_IMMUNE:
157                 mDownCounter--;
158                 if (mDownCounter <= 0) {
159                     mState = STATE_WAITING_FOR_SIGNAL;
160                 }
161                 break;
162 
163             case STATE_WAITING_FOR_SIGNAL:
164                 if (peak > mThreshold) {
165                     mState = STATE_WAITING_FOR_LOCK;
166                     //ALOGD("%5d: switch to STATE_WAITING_FOR_LOCK", mFrameCounter);
167                     resetAccumulator();
168                 }
169                 break;
170 
171             case STATE_WAITING_FOR_LOCK:
172                 mSinAccumulator += static_cast<double>(sample) * sinf(mInputPhase);
173                 mCosAccumulator += static_cast<double>(sample) * cosf(mInputPhase);
174                 mFramesAccumulated++;
175                 // Must be a multiple of the period or the calculation will not be accurate.
176                 if (mFramesAccumulated == mSinePeriod * PERIODS_NEEDED_FOR_LOCK) {
177                     setMagnitude(calculateMagnitudePhase(&mPhaseOffset));
178 //                    ALOGD("%s() mag = %f, offset = %f, prev = %f",
179 //                            __func__, mMagnitude, mPhaseOffset, mPreviousPhaseOffset);
180                     if (mMagnitude > mThreshold) {
181                         if (abs(mPhaseOffset) < kMaxPhaseError) {
182                             mState = STATE_LOCKED;
183 //                            ALOGD("%5d: switch to STATE_LOCKED", mFrameCounter);
184                         }
185                         // Adjust mInputPhase to match measured phase
186                         mInputPhase += mPhaseOffset;
187                     }
188                     resetAccumulator();
189                 }
190                 incrementInputPhase();
191                 break;
192 
193             case STATE_LOCKED: {
194                 // Predict next sine value
195                 double predicted = sinf(mInputPhase) * mMagnitude;
196                 double diff = predicted - sample;
197                 double absDiff = fabs(diff);
198                 mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff);
199                 if (absDiff > mScaledTolerance) {
200                     result = ERROR_GLITCHES;
201                     onGlitchStart();
202 //                    LOGI("diff glitch detected, absDiff = %g", absDiff);
203                 } else {
204                     mSumSquareSignal += predicted * predicted;
205                     mSumSquareNoise += diff * diff;
206 
207 
208                     // Track incoming signal and slowly adjust magnitude to account
209                     // for drift in the DRC or AGC.
210                     // Must be a multiple of the period or the calculation will not be accurate.
211                     if (transformSample(sample, mInputPhase)) {
212                         mMeanSquareNoise = mSumSquareNoise * mInverseSinePeriod;
213                         mMeanSquareSignal = mSumSquareSignal * mInverseSinePeriod;
214                         resetAccumulator();
215 
216                         if (abs(mPhaseOffset) > kMaxPhaseError) {
217                             result = ERROR_GLITCHES;
218                             onGlitchStart();
219                             ALOGD("phase glitch detected, phaseOffset = %g", mPhaseOffset);
220                         } else if (mMagnitude < mThreshold) {
221                             result = ERROR_GLITCHES;
222                             onGlitchStart();
223                             ALOGD("magnitude glitch detected, mMagnitude = %g", mMagnitude);
224                         }
225                     }
226                 }
227                 incrementInputPhase();
228             } break;
229 
230             case STATE_GLITCHING: {
231                 // Predict next sine value
232                 mGlitchLength++;
233                 double predicted = sinf(mInputPhase) * mMagnitude;
234                 double diff = predicted - sample;
235                 double absDiff = fabs(diff);
236                 mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff);
237                 if (absDiff < mScaledTolerance) { // close enough?
238                     // If we get a full sine period of non-glitch samples in a row then consider the glitch over.
239                     // We don't want to just consider a zero crossing the end of a glitch.
240                     if (mNonGlitchCount++ > mSinePeriod) {
241                         onGlitchEnd();
242                     }
243                 } else {
244                     mNonGlitchCount = 0;
245                     if (mGlitchLength > (4 * mSinePeriod)) {
246                         relock();
247                     }
248                 }
249                 incrementInputPhase();
250             } break;
251 
252             case NUM_STATES: // not a real state
253                 break;
254         }
255 
256         mFrameCounter++;
257 
258         return result;
259     }
260 
261     // advance and wrap phase
incrementInputPhase()262     void incrementInputPhase() {
263         mInputPhase += mPhaseIncrement;
264         if (mInputPhase > M_PI) {
265             mInputPhase -= (2.0 * M_PI);
266         }
267     }
268 
isOutputEnabled()269     bool isOutputEnabled() override { return mState != STATE_IDLE; }
270 
onGlitchStart()271     void onGlitchStart() {
272         mGlitchCount++;
273 //        ALOGD("%5d: STARTED a glitch # %d", mFrameCounter, mGlitchCount);
274         mState = STATE_GLITCHING;
275         mGlitchLength = 1;
276         mNonGlitchCount = 0;
277         mLastGlitchPosition = mInfiniteRecording.getTotalWritten();
278     }
279 
onGlitchEnd()280     void onGlitchEnd() {
281 //        ALOGD("%5d: ENDED a glitch # %d, length = %d", mFrameCounter, mGlitchCount, mGlitchLength);
282         mState = STATE_LOCKED;
283         resetAccumulator();
284     }
285 
286     // reset the sine wave detector
resetAccumulator()287     void resetAccumulator() override {
288         BaseSineAnalyzer::resetAccumulator();
289         mSumSquareSignal = 0.0;
290         mSumSquareNoise = 0.0;
291     }
292 
relock()293     void relock() {
294 //        ALOGD("relock: %d because of a very long %d glitch", mFrameCounter, mGlitchLength);
295         mState = STATE_WAITING_FOR_LOCK;
296         resetAccumulator();
297     }
298 
reset()299     void reset() override {
300         BaseSineAnalyzer::reset();
301         mState = STATE_IDLE;
302         mDownCounter = IDLE_FRAME_COUNT;
303     }
304 
prepareToTest()305     void prepareToTest() override {
306         BaseSineAnalyzer::prepareToTest();
307         mGlitchCount = 0;
308         mMaxGlitchDelta = 0.0;
309         for (int i = 0; i < NUM_STATES; i++) {
310             mStateFrameCounters[i] = 0;
311         }
312     }
313 
getLastGlitch(float * buffer,int32_t length)314     int32_t getLastGlitch(float *buffer, int32_t length) {
315         return mInfiniteRecording.readFrom(buffer, mLastGlitchPosition - 32, length);
316     }
317 
318 private:
319 
320     // These must match the values in GlitchActivity.java
321     enum sine_state_t {
322         STATE_IDLE,               // beginning
323         STATE_IMMUNE,             // ignoring input, waiting for HW to settle
324         STATE_WAITING_FOR_SIGNAL, // looking for a loud signal
325         STATE_WAITING_FOR_LOCK,   // trying to lock onto the phase of the sine
326         STATE_LOCKED,             // locked on the sine wave, looking for glitches
327         STATE_GLITCHING,          // locked on the sine wave but glitching
328         NUM_STATES
329     };
330 
331     enum constants {
332         // Arbitrary durations, assuming 48000 Hz
333         IDLE_FRAME_COUNT = 48 * 100,
334         IMMUNE_FRAME_COUNT = 48 * 100,
335         PERIODS_NEEDED_FOR_LOCK = 8,
336         MIN_SNR_DB = 65
337     };
338 
339     static constexpr double kMaxPhaseError = M_PI * 0.05;
340 
341     double  mThreshold = 0.005;
342 
343     int32_t mStateFrameCounters[NUM_STATES];
344     sine_state_t  mState = STATE_IDLE;
345     int64_t       mLastGlitchPosition;
346 
347     double  mInputPhase = 0.0;
348     double  mMaxGlitchDelta = 0.0;
349     int32_t mGlitchCount = 0;
350     int32_t mNonGlitchCount = 0;
351     int32_t mGlitchLength = 0;
352     int     mDownCounter = IDLE_FRAME_COUNT;
353     int32_t mFrameCounter = 0;
354 
355     int32_t mForceGlitchDuration = 0; // if > 0 then force a glitch for debugging
356     int32_t mForceGlitchCounter = 4 * 48000; // count down and trigger at zero
357 
358     // measure background noise continuously as a deviation from the expected signal
359     double  mSumSquareSignal = 0.0;
360     double  mSumSquareNoise = 0.0;
361     double  mMeanSquareSignal = 0.0;
362     double  mMeanSquareNoise = 0.0;
363 
364     PeakDetector  mPeakFollower;
365 };
366 
367 
368 #endif //ANALYZER_GLITCH_ANALYZER_H
369