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, mInputPhase)) { 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 incrementInputPhase(); 253 } break; 254 255 case STATE_GLITCHING: { 256 // Predict next sine value 257 double predicted = sinf(mInputPhase) * mMagnitude; 258 double diff = predicted - sample; 259 double absDiff = fabs(diff); 260 mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff); 261 if (absDiff > mScaledTolerance) { // bad frame 262 mConsecutiveBadFrames++; 263 mConsecutiveGoodFrames = 0; 264 mGlitchLength++; 265 if (mGlitchLength > maxMeasurableGlitchLength()) { 266 onGlitchTerminated(); 267 } 268 } else { // good frame 269 mConsecutiveBadFrames = 0; 270 mConsecutiveGoodFrames++; 271 // If we get a full sine period of good samples in a row then consider the glitch over. 272 // We don't want to just consider a zero crossing the end of a glitch. 273 if (mConsecutiveGoodFrames > mSinePeriod) { 274 onGlitchEnd(); 275 } 276 } 277 incrementInputPhase(); 278 } break; 279 280 case NUM_STATES: // not a real state 281 break; 282 } 283 284 mFrameCounter++; 285 286 return result; 287 } 288 maxMeasurableGlitchLength()289 int maxMeasurableGlitchLength() const { return 2 * mSinePeriod; } 290 291 // advance and wrap phase incrementInputPhase()292 void incrementInputPhase() { 293 mInputPhase += mPhaseIncrement; 294 if (mInputPhase > M_PI) { 295 mInputPhase -= (2.0 * M_PI); 296 } 297 } 298 isOutputEnabled()299 bool isOutputEnabled() override { return mState != STATE_IDLE; } 300 onGlitchStart()301 void onGlitchStart() { 302 mState = STATE_GLITCHING; 303 mGlitchLength = 1; 304 mLastGlitchPosition = mInfiniteRecording.getTotalWritten(); 305 ALOGD("%5d: STARTED a glitch # %d, pos = %5d", 306 mFrameCounter, mGlitchCount, (int)mLastGlitchPosition); 307 ALOGD("glitch mSinePeriod = %d", mSinePeriod); 308 } 309 310 /** 311 * Give up waiting for a glitch to end and try to resync. 312 */ onGlitchTerminated()313 void onGlitchTerminated() { 314 mGlitchCount++; 315 ALOGD("%5d: TERMINATED a glitch # %d, length = %d", mFrameCounter, mGlitchCount, mGlitchLength); 316 // We don't know how long the glitch really is so set the length to -1. 317 mGlitchLength = -1; 318 mState = STATE_WAITING_FOR_LOCK; 319 resetAccumulator(); 320 } 321 onGlitchEnd()322 void onGlitchEnd() { 323 mGlitchCount++; 324 ALOGD("%5d: ENDED a glitch # %d, length = %d", mFrameCounter, mGlitchCount, mGlitchLength); 325 mState = STATE_LOCKED; 326 resetAccumulator(); 327 } 328 329 // reset the sine wave detector resetAccumulator()330 void resetAccumulator() override { 331 BaseSineAnalyzer::resetAccumulator(); 332 } 333 reset()334 void reset() override { 335 BaseSineAnalyzer::reset(); 336 mState = STATE_IDLE; 337 mDownCounter = IDLE_FRAME_COUNT; 338 } 339 prepareToTest()340 void prepareToTest() override { 341 BaseSineAnalyzer::prepareToTest(); 342 mGlitchCount = 0; 343 mGlitchLength = 0; 344 mMaxGlitchDelta = 0.0; 345 for (int i = 0; i < NUM_STATES; i++) { 346 mStateFrameCounters[i] = 0; 347 } 348 } 349 getLastGlitch(float * buffer,int32_t length)350 int32_t getLastGlitch(float *buffer, int32_t length) { 351 const int margin = mSinePeriod; 352 int32_t numSamples = mInfiniteRecording.readFrom(buffer, 353 mLastGlitchPosition - margin, 354 length); 355 ALOGD("%s: glitch at %d, edge = %7.4f, %7.4f, %7.4f", 356 __func__, (int)mLastGlitchPosition, 357 buffer[margin - 1], buffer[margin], buffer[margin+1]); 358 return numSamples; 359 } 360 getRecentSamples(float * buffer,int32_t length)361 int32_t getRecentSamples(float *buffer, int32_t length) { 362 int firstSample = mInfiniteRecording.getTotalWritten() - length; 363 int32_t numSamples = mInfiniteRecording.readFrom(buffer, 364 firstSample, 365 length); 366 return numSamples; 367 } 368 setForcedGlitchDuration(int frames)369 void setForcedGlitchDuration(int frames) { 370 mForceGlitchDurationFrames = frames; 371 } 372 373 private: 374 375 // These must match the values in GlitchActivity.java 376 enum sine_state_t { 377 STATE_IDLE, // beginning 378 STATE_IMMUNE, // ignoring input, waiting for HW to settle 379 STATE_WAITING_FOR_SIGNAL, // looking for a loud signal 380 STATE_WAITING_FOR_LOCK, // trying to lock onto the phase of the sine 381 STATE_LOCKED, // locked on the sine wave, looking for glitches 382 STATE_GLITCHING, // locked on the sine wave but glitching 383 NUM_STATES 384 }; 385 386 enum constants { 387 // Arbitrary durations, assuming 48000 Hz 388 IDLE_FRAME_COUNT = 48 * 100, 389 IMMUNE_FRAME_COUNT = 48 * 100, 390 PERIODS_NEEDED_FOR_LOCK = 8, 391 MIN_SNR_DB = 65 392 }; 393 394 static constexpr double kMaxPhaseError = M_PI * 0.05; 395 396 double mThreshold = 0.005; 397 398 int32_t mStateFrameCounters[NUM_STATES]; 399 sine_state_t mState = STATE_IDLE; 400 int64_t mLastGlitchPosition; 401 402 double mInputPhase = 0.0; 403 double mMaxGlitchDelta = 0.0; 404 int32_t mGlitchCount = 0; 405 int32_t mConsecutiveBadFrames = 0; 406 int32_t mConsecutiveGoodFrames = 0; 407 int32_t mGlitchLength = 0; 408 int mDownCounter = IDLE_FRAME_COUNT; 409 int32_t mFrameCounter = 0; 410 411 int32_t mForceGlitchDurationFrames = 0; // if > 0 then force a glitch for debugging 412 static constexpr int32_t kForceGlitchPeriod = 2 * 48000; // How often we glitch 413 static constexpr float kForceGlitchOffset = 0.20f; 414 int32_t mForceGlitchCounter = kForceGlitchPeriod; // count down and trigger at zero 415 416 // measure background noise continuously as a deviation from the expected signal 417 double mSumSquareSignal = 0.0; 418 double mSumSquareNoise = 0.0; 419 double mMeanSquareSignal = 0.0; 420 double mMeanSquareNoise = 0.0; 421 422 PeakDetector mPeakFollower; 423 }; 424 425 426 #endif //ANALYZER_GLITCH_ANALYZER_H 427