LoopbackAnalyzer.h revision 68326fe017a18957419a81489b67f75831f9a674
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/** 18 * Tools for measuring latency and for detecting glitches. 19 * These classes are pure math and can be used with any audio system. 20 */ 21 22#ifndef AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H 23#define AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H 24 25#include <algorithm> 26#include <assert.h> 27#include <cctype> 28#include <math.h> 29#include <stdio.h> 30#include <stdlib.h> 31#include <unistd.h> 32 33// Tag for machine readable results as property = value pairs 34#define LOOPBACK_RESULT_TAG "RESULT: " 35#define LOOPBACK_SAMPLE_RATE 48000 36 37#define MILLIS_PER_SECOND 1000 38 39#define MAX_ZEROTH_PARTIAL_BINS 40 40 41static const float s_Impulse[] = { 42 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, // silence on each side of the impulse 43 0.5f, 0.9999f, 0.0f, -0.9999, -0.5f, // bipolar 44 -0.2f, 0.0f, 0.0f, 0.0f, 0.0f 45}; 46 47class PseudoRandom { 48public: 49 PseudoRandom() {} 50 PseudoRandom(int64_t seed) 51 : mSeed(seed) 52 {} 53 54 /** 55 * Returns the next random double from -1.0 to 1.0 56 * 57 * @return value from -1.0 to 1.0 58 */ 59 double nextRandomDouble() { 60 return nextRandomInteger() * (0.5 / (((int32_t)1) << 30)); 61 } 62 63 /** Calculate random 32 bit number using linear-congruential method. */ 64 int32_t nextRandomInteger() { 65 // Use values for 64-bit sequence from MMIX by Donald Knuth. 66 mSeed = (mSeed * (int64_t)6364136223846793005) + (int64_t)1442695040888963407; 67 return (int32_t) (mSeed >> 32); // The higher bits have a longer sequence. 68 } 69 70private: 71 int64_t mSeed = 99887766; 72}; 73 74static double calculateCorrelation(const float *a, 75 const float *b, 76 int windowSize) 77{ 78 double correlation = 0.0; 79 double sumProducts = 0.0; 80 double sumSquares = 0.0; 81 82 // Correlate a against b. 83 for (int i = 0; i < windowSize; i++) { 84 float s1 = a[i]; 85 float s2 = b[i]; 86 // Use a normalized cross-correlation. 87 sumProducts += s1 * s2; 88 sumSquares += ((s1 * s1) + (s2 * s2)); 89 } 90 91 if (sumSquares >= 0.00000001) { 92 correlation = (float) (2.0 * sumProducts / sumSquares); 93 } 94 return correlation; 95} 96 97static int calculateCorrelations(const float *haystack, int haystackSize, 98 const float *needle, int needleSize, 99 float *results, int resultSize) 100{ 101 int maxCorrelations = haystackSize - needleSize; 102 int numCorrelations = std::min(maxCorrelations, resultSize); 103 104 for (int ic = 0; ic < numCorrelations; ic++) { 105 double correlation = calculateCorrelation(&haystack[ic], needle, needleSize); 106 results[ic] = correlation; 107 } 108 109 return numCorrelations; 110} 111 112/*==========================================================================================*/ 113/** 114 * Scan until we get a correlation of a single scan that goes over the tolerance level, 115 * peaks then drops back down. 116 */ 117static double findFirstMatch(const float *haystack, int haystackSize, 118 const float *needle, int needleSize, double threshold ) 119{ 120 int ic; 121 // How many correlations can we calculate? 122 int numCorrelations = haystackSize - needleSize; 123 double maxCorrelation = 0.0; 124 int peakIndex = -1; 125 double location = -1.0; 126 const double backThresholdScaler = 0.5; 127 128 for (ic = 0; ic < numCorrelations; ic++) { 129 double correlation = calculateCorrelation(&haystack[ic], needle, needleSize); 130 131 if( (correlation > maxCorrelation) ) { 132 maxCorrelation = correlation; 133 peakIndex = ic; 134 } 135 136 //printf("PaQa_FindFirstMatch: ic = %4d, correlation = %8f, maxSum = %8f\n", 137 // ic, correlation, maxSum ); 138 // Are we past what we were looking for? 139 if((maxCorrelation > threshold) && (correlation < backThresholdScaler * maxCorrelation)) { 140 location = peakIndex; 141 break; 142 } 143 } 144 145 return location; 146} 147 148typedef struct LatencyReport_s { 149 double latencyInFrames; 150 double confidence; 151} LatencyReport; 152 153// Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental. 154// Using first echo instead of the original impulse for a better match. 155static int measureLatencyFromEchos(const float *haystack, int haystackSize, 156 const float *needle, int needleSize, 157 LatencyReport *report) { 158 const double threshold = 0.1; 159 160 // Find first peak 161 int first = (int) (findFirstMatch(haystack, 162 haystackSize, 163 needle, 164 needleSize, 165 threshold) + 0.5); 166 167 // Use first echo as the needle for the other echos because 168 // it will be more similar. 169 needle = &haystack[first]; 170 int again = (int) (findFirstMatch(haystack, 171 haystackSize, 172 needle, 173 needleSize, 174 threshold) + 0.5); 175 176 printf("first = %d, again at %d\n", first, again); 177 first = again; 178 179 // Allocate results array 180 int remaining = haystackSize - first; 181 const int maxReasonableLatencyFrames = 48000 * 2; // arbitrary but generous value 182 int numCorrelations = std::min(remaining, maxReasonableLatencyFrames); 183 float *correlations = new float[numCorrelations]; 184 float *harmonicSums = new float[numCorrelations](); // set to zero 185 186 // Generate correlation for every position. 187 numCorrelations = calculateCorrelations(&haystack[first], remaining, 188 needle, needleSize, 189 correlations, numCorrelations); 190 191 // Add higher harmonics mapped onto lower harmonics. 192 // This reinforces the "fundamental" echo. 193 const int numEchoes = 10; 194 for (int partial = 1; partial < numEchoes; partial++) { 195 for (int i = 0; i < numCorrelations; i++) { 196 harmonicSums[i / partial] += correlations[i] / partial; 197 } 198 } 199 200 // Find highest peak in correlation array. 201 float maxCorrelation = 0.0; 202 float sumOfPeaks = 0.0; 203 int peakIndex = 0; 204 const int skip = MAX_ZEROTH_PARTIAL_BINS; // skip low bins 205 for (int i = skip; i < numCorrelations; i++) { 206 if (harmonicSums[i] > maxCorrelation) { 207 maxCorrelation = harmonicSums[i]; 208 sumOfPeaks += maxCorrelation; 209 peakIndex = i; 210 printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex); 211 } 212 } 213 214 report->latencyInFrames = peakIndex; 215 if (sumOfPeaks < 0.0001) { 216 report->confidence = 0.0; 217 } else { 218 report->confidence = maxCorrelation / sumOfPeaks; 219 } 220 221 delete[] correlations; 222 delete[] harmonicSums; 223 return 0; 224} 225 226class AudioRecording 227{ 228public: 229 AudioRecording() { 230 } 231 ~AudioRecording() { 232 delete[] mData; 233 } 234 235 void allocate(int maxFrames) { 236 delete[] mData; 237 mData = new float[maxFrames]; 238 mMaxFrames = maxFrames; 239 } 240 241 // Write SHORT data from the first channel. 242 int write(int16_t *inputData, int inputChannelCount, int numFrames) { 243 // stop at end of buffer 244 if ((mFrameCounter + numFrames) > mMaxFrames) { 245 numFrames = mMaxFrames - mFrameCounter; 246 } 247 for (int i = 0; i < numFrames; i++) { 248 mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768); 249 } 250 return numFrames; 251 } 252 253 // Write FLOAT data from the first channel. 254 int write(float *inputData, int inputChannelCount, int numFrames) { 255 // stop at end of buffer 256 if ((mFrameCounter + numFrames) > mMaxFrames) { 257 numFrames = mMaxFrames - mFrameCounter; 258 } 259 for (int i = 0; i < numFrames; i++) { 260 mData[mFrameCounter++] = inputData[i * inputChannelCount]; 261 } 262 return numFrames; 263 } 264 265 int size() { 266 return mFrameCounter; 267 } 268 269 float *getData() { 270 return mData; 271 } 272 273 int save(const char *fileName, bool writeShorts = true) { 274 int written = 0; 275 const int chunkSize = 64; 276 FILE *fid = fopen(fileName, "wb"); 277 if (fid == NULL) { 278 return -errno; 279 } 280 281 if (writeShorts) { 282 int16_t buffer[chunkSize]; 283 int32_t framesLeft = mFrameCounter; 284 int32_t cursor = 0; 285 while (framesLeft) { 286 int32_t framesToWrite = framesLeft < chunkSize ? framesLeft : chunkSize; 287 for (int i = 0; i < framesToWrite; i++) { 288 buffer[i] = (int16_t) (mData[cursor++] * 32767); 289 } 290 written += fwrite(buffer, sizeof(int16_t), framesToWrite, fid); 291 framesLeft -= framesToWrite; 292 } 293 } else { 294 written = (int) fwrite(mData, sizeof(float), mFrameCounter, fid); 295 } 296 fclose(fid); 297 return written; 298 } 299 300private: 301 float *mData = nullptr; 302 int32_t mFrameCounter = 0; 303 int32_t mMaxFrames = 0; 304}; 305 306// ==================================================================================== 307class LoopbackProcessor { 308public: 309 virtual ~LoopbackProcessor() = default; 310 311 312 virtual void reset() {} 313 314 virtual void process(float *inputData, int inputChannelCount, 315 float *outputData, int outputChannelCount, 316 int numFrames) = 0; 317 318 319 virtual void report() = 0; 320 321 virtual void printStatus() {}; 322 323 virtual bool isDone() { 324 return false; 325 } 326 327 void setSampleRate(int32_t sampleRate) { 328 mSampleRate = sampleRate; 329 } 330 331 int32_t getSampleRate() { 332 return mSampleRate; 333 } 334 335 // Measure peak amplitude of buffer. 336 static float measurePeakAmplitude(float *inputData, int inputChannelCount, int numFrames) { 337 float peak = 0.0f; 338 for (int i = 0; i < numFrames; i++) { 339 float pos = fabs(*inputData); 340 if (pos > peak) { 341 peak = pos; 342 } 343 inputData += inputChannelCount; 344 } 345 return peak; 346 } 347 348 349private: 350 int32_t mSampleRate = LOOPBACK_SAMPLE_RATE; 351}; 352 353class PeakDetector { 354public: 355 float process(float input) { 356 float output = mPrevious * mDecay; 357 if (input > output) { 358 output = input; 359 } 360 mPrevious = output; 361 return output; 362 } 363 364private: 365 float mDecay = 0.99f; 366 float mPrevious = 0.0f; 367}; 368 369 370static void printAudioScope(float sample) { 371 const int maxStars = 80 372 ; // arbitrary, fits on one line 373 char c = '*'; 374 if (sample < -1.0) { 375 sample = -1.0; 376 c = '$'; 377 } else if (sample > 1.0) { 378 sample = 1.0; 379 c = '$'; 380 } 381 int numSpaces = (int) (((sample + 1.0) * 0.5) * maxStars); 382 for (int i = 0; i < numSpaces; i++) { 383 putchar(' '); 384 } 385 printf("%c\n", c); 386} 387 388// ==================================================================================== 389/** 390 * Measure latency given a loopback stream data. 391 * Uses a state machine to cycle through various stages including: 392 * 393 */ 394class EchoAnalyzer : public LoopbackProcessor { 395public: 396 397 EchoAnalyzer() : LoopbackProcessor() { 398 audioRecorder.allocate(2 * LOOPBACK_SAMPLE_RATE); 399 } 400 401 void reset() override { 402 mDownCounter = 200; 403 mLoopCounter = 0; 404 mMeasuredLoopGain = 0.0f; 405 mEchoGain = 1.0f; 406 mState = STATE_INITIAL_SILENCE; 407 } 408 409 virtual bool isDone() { 410 return mState == STATE_DONE; 411 } 412 413 void setGain(float gain) { 414 mEchoGain = gain; 415 } 416 417 float getGain() { 418 return mEchoGain; 419 } 420 421 void report() override { 422 423 printf("EchoAnalyzer ---------------\n"); 424 printf(LOOPBACK_RESULT_TAG "measured.gain = %f\n", mMeasuredLoopGain); 425 printf(LOOPBACK_RESULT_TAG "echo.gain = %f\n", mEchoGain); 426 printf(LOOPBACK_RESULT_TAG "frame.count = %d\n", mFrameCounter); 427 printf(LOOPBACK_RESULT_TAG "test.state = %d\n", mState); 428 if (mMeasuredLoopGain >= 0.9999) { 429 printf(" ERROR - clipping, turn down volume slightly\n"); 430 } else { 431 const float *needle = s_Impulse; 432 int needleSize = (int) (sizeof(s_Impulse) / sizeof(float)); 433 float *haystack = audioRecorder.getData(); 434 int haystackSize = audioRecorder.size(); 435 measureLatencyFromEchos(haystack, haystackSize, needle, needleSize, &latencyReport); 436 if (latencyReport.confidence < 0.01) { 437 printf(" ERROR - confidence too low = %f\n", latencyReport.confidence); 438 } else { 439 double latencyMillis = 1000.0 * latencyReport.latencyInFrames / getSampleRate(); 440 printf(LOOPBACK_RESULT_TAG "latency.frames = %8.2f\n", latencyReport.latencyInFrames); 441 printf(LOOPBACK_RESULT_TAG "latency.msec = %8.2f\n", latencyMillis); 442 printf(LOOPBACK_RESULT_TAG "latency.confidence = %8.6f\n", latencyReport.confidence); 443 } 444 } 445 446 { 447#define ECHO_FILENAME "/data/oboe_echo.raw" 448 int written = audioRecorder.save(ECHO_FILENAME); 449 printf("Echo wrote %d mono samples to %s on Android device\n", written, ECHO_FILENAME); 450 } 451 } 452 453 void printStatus() override { 454 printf("state = %d, echo gain = %f ", mState, mEchoGain); 455 } 456 457 static void sendImpulse(float *outputData, int outputChannelCount) { 458 for (float sample : s_Impulse) { 459 *outputData = sample; 460 outputData += outputChannelCount; 461 } 462 } 463 464 void process(float *inputData, int inputChannelCount, 465 float *outputData, int outputChannelCount, 466 int numFrames) override { 467 int channelsValid = std::min(inputChannelCount, outputChannelCount); 468 float peak = 0.0f; 469 int numWritten; 470 int numSamples; 471 472 echo_state_t nextState = mState; 473 474 switch (mState) { 475 case STATE_INITIAL_SILENCE: 476 // Output silence at the beginning. 477 numSamples = numFrames * outputChannelCount; 478 for (int i = 0; i < numSamples; i++) { 479 outputData[i] = 0; 480 } 481 if (mDownCounter-- <= 0) { 482 nextState = STATE_MEASURING_GAIN; 483 //printf("%5d: switch to STATE_MEASURING_GAIN\n", mLoopCounter); 484 mDownCounter = 8; 485 } 486 break; 487 488 case STATE_MEASURING_GAIN: 489 sendImpulse(outputData, outputChannelCount); 490 peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames); 491 // If we get several in a row then go to next state. 492 if (peak > mPulseThreshold) { 493 if (mDownCounter-- <= 0) { 494 nextState = STATE_WAITING_FOR_SILENCE; 495 //printf("%5d: switch to STATE_WAITING_FOR_SILENCE, measured peak = %f\n", 496 // mLoopCounter, peak); 497 mDownCounter = 8; 498 mMeasuredLoopGain = peak; // assumes original pulse amplitude is one 499 // Calculate gain that will give us a nice decaying echo. 500 mEchoGain = mDesiredEchoGain / mMeasuredLoopGain; 501 } 502 } else { 503 mDownCounter = 8; 504 } 505 break; 506 507 case STATE_WAITING_FOR_SILENCE: 508 // Output silence. 509 numSamples = numFrames * outputChannelCount; 510 for (int i = 0; i < numSamples; i++) { 511 outputData[i] = 0; 512 } 513 peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames); 514 // If we get several in a row then go to next state. 515 if (peak < mSilenceThreshold) { 516 if (mDownCounter-- <= 0) { 517 nextState = STATE_SENDING_PULSE; 518 //printf("%5d: switch to STATE_SENDING_PULSE\n", mLoopCounter); 519 mDownCounter = 8; 520 } 521 } else { 522 mDownCounter = 8; 523 } 524 break; 525 526 case STATE_SENDING_PULSE: 527 audioRecorder.write(inputData, inputChannelCount, numFrames); 528 sendImpulse(outputData, outputChannelCount); 529 nextState = STATE_GATHERING_ECHOS; 530 //printf("%5d: switch to STATE_GATHERING_ECHOS\n", mLoopCounter); 531 break; 532 533 case STATE_GATHERING_ECHOS: 534 numWritten = audioRecorder.write(inputData, inputChannelCount, numFrames); 535 peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames); 536 if (peak > mMeasuredLoopGain) { 537 mMeasuredLoopGain = peak; // AGC might be raising gain so adjust it on the fly. 538 // Recalculate gain that will give us a nice decaying echo. 539 mEchoGain = mDesiredEchoGain / mMeasuredLoopGain; 540 } 541 // Echo input to output. 542 for (int i = 0; i < numFrames; i++) { 543 int ic; 544 for (ic = 0; ic < channelsValid; ic++) { 545 outputData[ic] = inputData[ic] * mEchoGain; 546 } 547 for (; ic < outputChannelCount; ic++) { 548 outputData[ic] = 0; 549 } 550 inputData += inputChannelCount; 551 outputData += outputChannelCount; 552 } 553 if (numWritten < numFrames) { 554 nextState = STATE_DONE; 555 //printf("%5d: switch to STATE_DONE\n", mLoopCounter); 556 } 557 break; 558 559 case STATE_DONE: 560 default: 561 break; 562 } 563 564 mState = nextState; 565 mLoopCounter++; 566 } 567 568private: 569 570 enum echo_state_t { 571 STATE_INITIAL_SILENCE, 572 STATE_MEASURING_GAIN, 573 STATE_WAITING_FOR_SILENCE, 574 STATE_SENDING_PULSE, 575 STATE_GATHERING_ECHOS, 576 STATE_DONE 577 }; 578 579 int mDownCounter = 500; 580 int mLoopCounter = 0; 581 float mPulseThreshold = 0.02f; 582 float mSilenceThreshold = 0.002f; 583 float mMeasuredLoopGain = 0.0f; 584 float mDesiredEchoGain = 0.95f; 585 float mEchoGain = 1.0f; 586 echo_state_t mState = STATE_INITIAL_SILENCE; 587 int32_t mFrameCounter = 0; 588 589 AudioRecording audioRecorder; 590 LatencyReport latencyReport; 591 PeakDetector mPeakDetector; 592}; 593 594 595// ==================================================================================== 596/** 597 * Output a steady sinewave and analyze the return signal. 598 * 599 * Use a cosine transform to measure the predicted magnitude and relative phase of the 600 * looped back sine wave. Then generate a predicted signal and compare with the actual signal. 601 */ 602class SineAnalyzer : public LoopbackProcessor { 603public: 604 605 void report() override { 606 printf("SineAnalyzer ------------------\n"); 607 printf(LOOPBACK_RESULT_TAG "peak.amplitude = %7.5f\n", mPeakAmplitude); 608 printf(LOOPBACK_RESULT_TAG "sine.magnitude = %7.5f\n", mMagnitude); 609 printf(LOOPBACK_RESULT_TAG "phase.offset = %7.5f\n", mPhaseOffset); 610 printf(LOOPBACK_RESULT_TAG "ref.phase = %7.5f\n", mPhase); 611 printf(LOOPBACK_RESULT_TAG "frames.accumulated = %6d\n", mFramesAccumulated); 612 printf(LOOPBACK_RESULT_TAG "sine.period = %6d\n", mPeriod); 613 printf(LOOPBACK_RESULT_TAG "test.state = %6d\n", mState); 614 printf(LOOPBACK_RESULT_TAG "frame.count = %6d\n", mFrameCounter); 615 // Did we ever get a lock? 616 bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0); 617 if (!gotLock) { 618 printf("ERROR - failed to lock on reference sine tone\n"); 619 } else { 620 // Only print if meaningful. 621 printf(LOOPBACK_RESULT_TAG "glitch.count = %6d\n", mGlitchCount); 622 } 623 } 624 625 void printStatus() override { 626 printf(" state = %d, glitches = %d,", mState, mGlitchCount); 627 } 628 629 double calculateMagnitude(double *phasePtr = NULL) { 630 if (mFramesAccumulated == 0) { 631 return 0.0; 632 } 633 double sinMean = mSinAccumulator / mFramesAccumulated; 634 double cosMean = mCosAccumulator / mFramesAccumulated; 635 double magnitude = 2.0 * sqrt( (sinMean * sinMean) + (cosMean * cosMean )); 636 if( phasePtr != NULL ) 637 { 638 double phase = M_PI_2 - atan2( sinMean, cosMean ); 639 *phasePtr = phase; 640 } 641 return magnitude; 642 } 643 644 /** 645 * @param inputData contains microphone data with sine signal feedback 646 * @param outputData contains the reference sine wave 647 */ 648 void process(float *inputData, int inputChannelCount, 649 float *outputData, int outputChannelCount, 650 int numFrames) override { 651 float peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames); 652 if (peak > mPeakAmplitude) { 653 mPeakAmplitude = peak; 654 } 655 656 for (int i = 0; i < numFrames; i++) { 657 float sample = inputData[i * inputChannelCount]; 658 659 float sinOut = sinf(mPhase); 660 661 switch (mState) { 662 case STATE_IMMUNE: 663 case STATE_WAITING_FOR_SIGNAL: 664 break; 665 case STATE_WAITING_FOR_LOCK: 666 mSinAccumulator += sample * sinOut; 667 mCosAccumulator += sample * cosf(mPhase); 668 mFramesAccumulated++; 669 // Must be a multiple of the period or the calculation will not be accurate. 670 if (mFramesAccumulated == mPeriod * 4) { 671 mPhaseOffset = 0.0; 672 mMagnitude = calculateMagnitude(&mPhaseOffset); 673 if (mMagnitude > mThreshold) { 674 if (fabs(mPreviousPhaseOffset - mPhaseOffset) < 0.001) { 675 mState = STATE_LOCKED; 676 //printf("%5d: switch to STATE_LOCKED\n", mFrameCounter); 677 } 678 mPreviousPhaseOffset = mPhaseOffset; 679 } 680 resetAccumulator(); 681 } 682 break; 683 684 case STATE_LOCKED: { 685 // Predict next sine value 686 float predicted = sinf(mPhase + mPhaseOffset) * mMagnitude; 687 // printf(" predicted = %f, actual = %f\n", predicted, sample); 688 689 float diff = predicted - sample; 690 if (fabs(diff) > mTolerance) { 691 mGlitchCount++; 692 //printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n", 693 // mFrameCounter, mGlitchCount, predicted, sample); 694 mState = STATE_IMMUNE; 695 //printf("%5d: switch to STATE_IMMUNE\n", mFrameCounter); 696 mDownCounter = mPeriod; // Set duration of IMMUNE state. 697 } 698 } break; 699 } 700 701 // Output sine wave so we can measure it. 702 outputData[i * outputChannelCount] = (sinOut * mOutputAmplitude) 703 + (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude); 704 // printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut, mPhaseIncrement); 705 706 // advance and wrap phase 707 mPhase += mPhaseIncrement; 708 if (mPhase > M_PI) { 709 mPhase -= (2.0 * M_PI); 710 } 711 712 mFrameCounter++; 713 } 714 715 // Do these once per buffer. 716 switch (mState) { 717 case STATE_IMMUNE: 718 mDownCounter -= numFrames; 719 if (mDownCounter <= 0) { 720 mState = STATE_WAITING_FOR_SIGNAL; 721 //printf("%5d: switch to STATE_WAITING_FOR_SIGNAL\n", mFrameCounter); 722 } 723 break; 724 case STATE_WAITING_FOR_SIGNAL: 725 if (peak > mThreshold) { 726 mState = STATE_WAITING_FOR_LOCK; 727 //printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter); 728 resetAccumulator(); 729 } 730 break; 731 case STATE_WAITING_FOR_LOCK: 732 case STATE_LOCKED: 733 break; 734 } 735 736 } 737 738 void resetAccumulator() { 739 mFramesAccumulated = 0; 740 mSinAccumulator = 0.0; 741 mCosAccumulator = 0.0; 742 } 743 744 void reset() override { 745 mGlitchCount = 0; 746 mState = STATE_IMMUNE; 747 mPhaseIncrement = 2.0 * M_PI / mPeriod; 748 printf("phaseInc = %f for period %d\n", mPhaseIncrement, mPeriod); 749 resetAccumulator(); 750 } 751 752private: 753 754 enum sine_state_t { 755 STATE_IMMUNE, 756 STATE_WAITING_FOR_SIGNAL, 757 STATE_WAITING_FOR_LOCK, 758 STATE_LOCKED 759 }; 760 761 int mPeriod = 79; 762 double mPhaseIncrement = 0.0; 763 double mPhase = 0.0; 764 double mPhaseOffset = 0.0; 765 double mPreviousPhaseOffset = 0.0; 766 double mMagnitude = 0.0; 767 double mThreshold = 0.005; 768 double mTolerance = 0.01; 769 int32_t mFramesAccumulated = 0; 770 double mSinAccumulator = 0.0; 771 double mCosAccumulator = 0.0; 772 int32_t mGlitchCount = 0; 773 double mPeakAmplitude = 0.0; 774 int mDownCounter = 4000; 775 int32_t mFrameCounter = 0; 776 float mOutputAmplitude = 0.75; 777 778 PseudoRandom mWhiteNoise; 779 float mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC. 780 781 sine_state_t mState = STATE_IMMUNE; 782}; 783 784 785#undef LOOPBACK_SAMPLE_RATE 786#undef LOOPBACK_RESULT_TAG 787 788#endif /* AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H */ 789