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 getSinePeriod()53 int getSinePeriod() const { 54 return mSinePeriod; 55 } 56 getGlitchCount()57 int32_t getGlitchCount() const { 58 return mGlitchCount; 59 } 60 getGlitchLength()61 int32_t getGlitchLength() const { 62 return mGlitchLength; 63 } 64 getStateFrameCount(int state)65 int32_t getStateFrameCount(int state) const { 66 return mStateFrameCounters[state]; 67 } 68 getSignalToNoiseDB()69 double getSignalToNoiseDB() { 70 static const double threshold = 1.0e-14; 71 if (mState != STATE_LOCKED 72 || mMeanSquareSignal < threshold 73 || mMeanSquareNoise < threshold) { 74 return -999.0; // error indicator 75 } else { 76 double signalToNoise = mMeanSquareSignal / mMeanSquareNoise; // power ratio 77 double signalToNoiseDB = 10.0 * log(signalToNoise); 78 if (signalToNoiseDB < static_cast<float>(MIN_SNR_DB)) { 79 setResult(ERROR_VOLUME_TOO_LOW); 80 } 81 return signalToNoiseDB; 82 } 83 } 84 analyze()85 std::string analyze() override { 86 std::stringstream report; 87 report << "GlitchAnalyzer ------------------\n"; 88 report << LOOPBACK_RESULT_TAG "peak.amplitude = " << std::setw(8) 89 << getPeakAmplitude() << "\n"; 90 report << LOOPBACK_RESULT_TAG "sine.magnitude = " << std::setw(8) 91 << getSineAmplitude() << "\n"; 92 report << LOOPBACK_RESULT_TAG "rms.noise = " << std::setw(8) 93 << mMeanSquareNoise << "\n"; 94 report << LOOPBACK_RESULT_TAG "signal.to.noise.db = " << std::setw(8) 95 << getSignalToNoiseDB() << "\n"; 96 report << LOOPBACK_RESULT_TAG "frames.accumulated = " << std::setw(8) 97 << mFramesAccumulated << "\n"; 98 report << LOOPBACK_RESULT_TAG "sine.period = " << std::setw(8) 99 << mSinePeriod << "\n"; 100 report << LOOPBACK_RESULT_TAG "test.state = " << std::setw(8) 101 << mState << "\n"; 102 report << LOOPBACK_RESULT_TAG "frame.count = " << std::setw(8) 103 << mFrameCounter << "\n"; 104 // Did we ever get a lock? 105 bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0); 106 if (!gotLock) { 107 report << "ERROR - failed to lock on reference sine tone.\n"; 108 setResult(ERROR_NO_LOCK); 109 } else { 110 // Only print if meaningful. 111 report << LOOPBACK_RESULT_TAG "glitch.count = " << std::setw(8) 112 << mGlitchCount << "\n"; 113 report << LOOPBACK_RESULT_TAG "max.glitch = " << std::setw(8) 114 << mMaxGlitchDelta << "\n"; 115 if (mGlitchCount > 0) { 116 report << "ERROR - number of glitches > 0\n"; 117 setResult(ERROR_GLITCHES); 118 } 119 } 120 return report.str(); 121 } 122 printStatus()123 void printStatus() override { 124 ALOGD("st = %d, #gl = %3d,", mState, mGlitchCount); 125 } 126 127 /** 128 * @param frameData contains microphone data with sine signal feedback 129 * @param channelCount 130 */ processInputFrame(const float * frameData,int)131 result_code processInputFrame(const float *frameData, int /* channelCount */) override { 132 result_code result = RESULT_OK; 133 134 float sample = frameData[getInputChannel()]; 135 136 // Force a periodic glitch to test the detector! 137 if (mForceGlitchDurationFrames > 0) { 138 if (mForceGlitchCounter == 0) { 139 ALOGE("%s: finish a glitch!!", __func__); 140 mForceGlitchCounter = kForceGlitchPeriod; 141 } else if (mForceGlitchCounter <= mForceGlitchDurationFrames) { 142 // Force an abrupt offset. 143 sample += (sample > 0.0) ? -kForceGlitchOffset : kForceGlitchOffset; 144 } 145 --mForceGlitchCounter; 146 } 147 148 float peak = mPeakFollower.process(sample); 149 mInfiniteRecording.write(sample); 150 151 mStateFrameCounters[mState]++; // count how many frames we are in each state 152 153 switch (mState) { 154 case STATE_IDLE: 155 mDownCounter--; 156 if (mDownCounter <= 0) { 157 mState = STATE_IMMUNE; 158 mDownCounter = IMMUNE_FRAME_COUNT; 159 mInputPhase = 0.0; // prevent spike at start 160 mOutputPhase = 0.0; 161 resetAccumulator(); 162 } 163 break; 164 165 case STATE_IMMUNE: 166 mDownCounter--; 167 if (mDownCounter <= 0) { 168 mState = STATE_WAITING_FOR_SIGNAL; 169 } 170 break; 171 172 case STATE_WAITING_FOR_SIGNAL: 173 if (peak > mThreshold) { 174 mState = STATE_WAITING_FOR_LOCK; 175 //ALOGD("%5d: switch to STATE_WAITING_FOR_LOCK", mFrameCounter); 176 resetAccumulator(); 177 } 178 break; 179 180 case STATE_WAITING_FOR_LOCK: 181 mSinAccumulator += static_cast<double>(sample) * sinf(mInputPhase); 182 mCosAccumulator += static_cast<double>(sample) * cosf(mInputPhase); 183 mFramesAccumulated++; 184 // Must be a multiple of the period or the calculation will not be accurate. 185 if (mFramesAccumulated == mSinePeriod * PERIODS_NEEDED_FOR_LOCK) { 186 double magnitude = calculateMagnitudePhase(&mPhaseOffset); 187 if (mPhaseOffset != kPhaseInvalid) { 188 setMagnitude(magnitude); 189 ALOGD("%s() mag = %f, mPhaseOffset = %f", 190 __func__, magnitude, mPhaseOffset); 191 if (mMagnitude > mThreshold) { 192 if (fabs(mPhaseOffset) < kMaxPhaseError) { 193 mState = STATE_LOCKED; 194 mConsecutiveBadFrames = 0; 195 // ALOGD("%5d: switch to STATE_LOCKED", mFrameCounter); 196 } 197 // Adjust mInputPhase to match measured phase 198 mInputPhase += mPhaseOffset; 199 } 200 } 201 resetAccumulator(); 202 } 203 incrementInputPhase(); 204 break; 205 206 case STATE_LOCKED: { 207 // Predict next sine value 208 double predicted = sinf(mInputPhase) * mMagnitude; 209 double diff = predicted - sample; 210 double absDiff = fabs(diff); 211 mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff); 212 if (absDiff > mScaledTolerance) { // bad frame 213 mConsecutiveBadFrames++; 214 mConsecutiveGoodFrames = 0; 215 LOGI("diff glitch frame #%d detected, absDiff = %g > %g", 216 mConsecutiveBadFrames, absDiff, mScaledTolerance); 217 if (mConsecutiveBadFrames > 0) { 218 result = ERROR_GLITCHES; 219 onGlitchStart(); 220 } 221 resetAccumulator(); 222 } else { // good frame 223 mConsecutiveBadFrames = 0; 224 mConsecutiveGoodFrames++; 225 226 mSumSquareSignal += predicted * predicted; 227 mSumSquareNoise += diff * diff; 228 229 // Track incoming signal and slowly adjust magnitude to account 230 // for drift in the DRC or AGC. 231 // Must be a multiple of the period or the calculation will not be accurate. 232 if (transformSample(sample)) { 233 // Adjust phase to account for sample rate drift. 234 mInputPhase += mPhaseOffset; 235 236 mMeanSquareNoise = mSumSquareNoise * mInverseSinePeriod; 237 mMeanSquareSignal = mSumSquareSignal * mInverseSinePeriod; 238 mSumSquareNoise = 0.0; 239 mSumSquareSignal = 0.0; 240 241 if (fabs(mPhaseOffset) > kMaxPhaseError) { 242 result = ERROR_GLITCHES; 243 onGlitchStart(); 244 ALOGD("phase glitch detected, phaseOffset = %g", mPhaseOffset); 245 } else if (mMagnitude < mThreshold) { 246 result = ERROR_GLITCHES; 247 onGlitchStart(); 248 ALOGD("magnitude glitch detected, mMagnitude = %g", mMagnitude); 249 } 250 } 251 } 252 } break; 253 254 case STATE_GLITCHING: { 255 // Predict next sine value 256 double predicted = sinf(mInputPhase) * mMagnitude; 257 double diff = predicted - sample; 258 double absDiff = fabs(diff); 259 mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff); 260 if (absDiff > mScaledTolerance) { // bad frame 261 mConsecutiveBadFrames++; 262 mConsecutiveGoodFrames = 0; 263 mGlitchLength++; 264 if (mGlitchLength > maxMeasurableGlitchLength()) { 265 onGlitchTerminated(); 266 } 267 } else { // good frame 268 mConsecutiveBadFrames = 0; 269 mConsecutiveGoodFrames++; 270 // If we get a full sine period of good samples in a row then consider the glitch over. 271 // We don't want to just consider a zero crossing the end of a glitch. 272 if (mConsecutiveGoodFrames > mSinePeriod) { 273 onGlitchEnd(); 274 } 275 } 276 incrementInputPhase(); 277 } break; 278 279 case NUM_STATES: // not a real state 280 break; 281 } 282 283 mFrameCounter++; 284 285 return result; 286 } 287 maxMeasurableGlitchLength()288 int maxMeasurableGlitchLength() const { return 2 * mSinePeriod; } 289 isOutputEnabled()290 bool isOutputEnabled() override { return mState != STATE_IDLE; } 291 onGlitchStart()292 void onGlitchStart() { 293 mState = STATE_GLITCHING; 294 mGlitchLength = 1; 295 mLastGlitchPosition = mInfiniteRecording.getTotalWritten(); 296 ALOGD("%5d: STARTED a glitch # %d, pos = %5d", 297 mFrameCounter, mGlitchCount, (int)mLastGlitchPosition); 298 ALOGD("glitch mSinePeriod = %d", mSinePeriod); 299 } 300 301 /** 302 * Give up waiting for a glitch to end and try to resync. 303 */ onGlitchTerminated()304 void onGlitchTerminated() { 305 mGlitchCount++; 306 ALOGD("%5d: TERMINATED a glitch # %d, length = %d", mFrameCounter, mGlitchCount, mGlitchLength); 307 // We don't know how long the glitch really is so set the length to -1. 308 mGlitchLength = -1; 309 mState = STATE_WAITING_FOR_LOCK; 310 resetAccumulator(); 311 } 312 onGlitchEnd()313 void onGlitchEnd() { 314 mGlitchCount++; 315 ALOGD("%5d: ENDED a glitch # %d, length = %d", mFrameCounter, mGlitchCount, mGlitchLength); 316 mState = STATE_LOCKED; 317 resetAccumulator(); 318 } 319 320 // reset the sine wave detector resetAccumulator()321 void resetAccumulator() override { 322 BaseSineAnalyzer::resetAccumulator(); 323 } 324 reset()325 void reset() override { 326 BaseSineAnalyzer::reset(); 327 mState = STATE_IDLE; 328 mDownCounter = IDLE_FRAME_COUNT; 329 } 330 prepareToTest()331 void prepareToTest() override { 332 BaseSineAnalyzer::prepareToTest(); 333 mGlitchCount = 0; 334 mGlitchLength = 0; 335 mMaxGlitchDelta = 0.0; 336 for (int i = 0; i < NUM_STATES; i++) { 337 mStateFrameCounters[i] = 0; 338 } 339 } 340 getLastGlitch(float * buffer,int32_t length)341 int32_t getLastGlitch(float *buffer, int32_t length) { 342 const int margin = mSinePeriod; 343 int32_t numSamples = mInfiniteRecording.readFrom(buffer, 344 mLastGlitchPosition - margin, 345 length); 346 ALOGD("%s: glitch at %d, edge = %7.4f, %7.4f, %7.4f", 347 __func__, (int)mLastGlitchPosition, 348 buffer[margin - 1], buffer[margin], buffer[margin+1]); 349 return numSamples; 350 } 351 getRecentSamples(float * buffer,int32_t length)352 int32_t getRecentSamples(float *buffer, int32_t length) { 353 int firstSample = mInfiniteRecording.getTotalWritten() - length; 354 int32_t numSamples = mInfiniteRecording.readFrom(buffer, 355 firstSample, 356 length); 357 return numSamples; 358 } 359 setForcedGlitchDuration(int frames)360 void setForcedGlitchDuration(int frames) { 361 mForceGlitchDurationFrames = frames; 362 } 363 364 private: 365 366 // These must match the values in GlitchActivity.java 367 enum sine_state_t { 368 STATE_IDLE, // beginning 369 STATE_IMMUNE, // ignoring input, waiting for HW to settle 370 STATE_WAITING_FOR_SIGNAL, // looking for a loud signal 371 STATE_WAITING_FOR_LOCK, // trying to lock onto the phase of the sine 372 STATE_LOCKED, // locked on the sine wave, looking for glitches 373 STATE_GLITCHING, // locked on the sine wave but glitching 374 NUM_STATES 375 }; 376 377 enum constants { 378 // Arbitrary durations, assuming 48000 Hz 379 IDLE_FRAME_COUNT = 48 * 100, 380 IMMUNE_FRAME_COUNT = 48 * 100, 381 PERIODS_NEEDED_FOR_LOCK = 8, 382 MIN_SNR_DB = 65 383 }; 384 385 static constexpr double kMaxPhaseError = M_PI * 0.05; 386 387 double mThreshold = 0.005; 388 389 int32_t mStateFrameCounters[NUM_STATES]; 390 sine_state_t mState = STATE_IDLE; 391 int64_t mLastGlitchPosition; 392 393 double mMaxGlitchDelta = 0.0; 394 int32_t mGlitchCount = 0; 395 int32_t mConsecutiveBadFrames = 0; 396 int32_t mConsecutiveGoodFrames = 0; 397 int32_t mGlitchLength = 0; 398 int mDownCounter = IDLE_FRAME_COUNT; 399 int32_t mFrameCounter = 0; 400 401 int32_t mForceGlitchDurationFrames = 0; // if > 0 then force a glitch for debugging 402 static constexpr int32_t kForceGlitchPeriod = 2 * 48000; // How often we glitch 403 static constexpr float kForceGlitchOffset = 0.20f; 404 int32_t mForceGlitchCounter = kForceGlitchPeriod; // count down and trigger at zero 405 406 // measure background noise continuously as a deviation from the expected signal 407 double mSumSquareSignal = 0.0; 408 double mSumSquareNoise = 0.0; 409 double mMeanSquareSignal = 0.0; 410 double mMeanSquareNoise = 0.0; 411 412 PeakDetector mPeakFollower; 413 }; 414 415 416 #endif //ANALYZER_GLITCH_ANALYZER_H 417