1 /* 2 * Copyright (C) 2020 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_BASE_SINE_ANALYZER_H 18 #define ANALYZER_BASE_SINE_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 28 /** 29 * Output a steady sine wave and analyze the return signal. 30 * 31 * Use a cosine transform to measure the predicted magnitude and relative phase of the 32 * looped back sine wave. Then generate a predicted signal and compare with the actual signal. 33 */ 34 class BaseSineAnalyzer : public LoopbackProcessor { 35 public: 36 BaseSineAnalyzer()37 BaseSineAnalyzer() 38 : LoopbackProcessor() 39 , mInfiniteRecording(64 * 1024) {} 40 41 isOutputEnabled()42 virtual bool isOutputEnabled() { return true; } 43 setMagnitude(double magnitude)44 void setMagnitude(double magnitude) { 45 mMagnitude = magnitude; 46 mScaledTolerance = mMagnitude * mTolerance; 47 } 48 getPhaseOffset()49 double getPhaseOffset() { 50 return mPhaseOffset; 51 } 52 getMagnitude()53 double getMagnitude() const { 54 return mMagnitude; 55 } 56 setInputChannel(int inputChannel)57 void setInputChannel(int inputChannel) { 58 mInputChannel = inputChannel; 59 } 60 getInputChannel()61 int getInputChannel() const { 62 return mInputChannel; 63 } 64 setOutputChannel(int outputChannel)65 void setOutputChannel(int outputChannel) { 66 mOutputChannel = outputChannel; 67 } 68 getOutputChannel()69 int getOutputChannel() const { 70 return mOutputChannel; 71 } 72 setNoiseAmplitude(double noiseAmplitude)73 void setNoiseAmplitude(double noiseAmplitude) { 74 mNoiseAmplitude = noiseAmplitude; 75 } 76 getNoiseAmplitude()77 double getNoiseAmplitude() const { 78 return mNoiseAmplitude; 79 } 80 getTolerance()81 double getTolerance() { 82 return mTolerance; 83 } 84 setTolerance(double tolerance)85 void setTolerance(double tolerance) { 86 mTolerance = tolerance; 87 } 88 89 // advance and wrap phase incrementOutputPhase()90 void incrementOutputPhase() { 91 mOutputPhase += mPhaseIncrement; 92 if (mOutputPhase > M_PI) { 93 mOutputPhase -= (2.0 * M_PI); 94 } 95 } 96 97 /** 98 * @param frameData upon return, contains the reference sine wave 99 * @param channelCount 100 */ processOutputFrame(float * frameData,int channelCount)101 result_code processOutputFrame(float *frameData, int channelCount) override { 102 float output = 0.0f; 103 // Output sine wave so we can measure it. 104 if (isOutputEnabled()) { 105 float sinOut = sinf(mOutputPhase); 106 incrementOutputPhase(); 107 output = (sinOut * mOutputAmplitude) 108 + (mWhiteNoise.nextRandomDouble() * getNoiseAmplitude()); 109 // ALOGD("sin(%f) = %f, %f\n", mOutputPhase, sinOut, mPhaseIncrement); 110 } 111 for (int i = 0; i < channelCount; i++) { 112 frameData[i] = (i == mOutputChannel) ? output : 0.0f; 113 } 114 return RESULT_OK; 115 } 116 117 /** 118 * Calculate the magnitude of the component of the input signal 119 * that matches the analysis frequency. 120 * Also calculate the phase that we can use to create a 121 * signal that matches that component. 122 * The phase will be between -PI and +PI. 123 */ 124 double calculateMagnitudePhase(double *phasePtr = nullptr) { 125 if (mFramesAccumulated == 0) { 126 return 0.0; 127 } 128 double sinMean = mSinAccumulator / mFramesAccumulated; 129 double cosMean = mCosAccumulator / mFramesAccumulated; 130 double magnitude = 2.0 * sqrt((sinMean * sinMean) + (cosMean * cosMean)); 131 if (phasePtr != nullptr) { 132 double phase = M_PI_2 - atan2(sinMean, cosMean); 133 *phasePtr = phase; 134 } 135 return magnitude; 136 } 137 138 /** 139 * Perform sin/cos analysis on each sample. 140 * Measure magnitude and phase on every period. 141 * @param sample 142 * @param referencePhase 143 * @return true if magnitude and phase updated 144 */ transformSample(float sample,float referencePhase)145 bool transformSample(float sample, float referencePhase) { 146 // Track incoming signal and slowly adjust magnitude to account 147 // for drift in the DRC or AGC. 148 mSinAccumulator += static_cast<double>(sample) * sinf(referencePhase); 149 mCosAccumulator += static_cast<double>(sample) * cosf(referencePhase); 150 mFramesAccumulated++; 151 // Must be a multiple of the period or the calculation will not be accurate. 152 if (mFramesAccumulated == mSinePeriod) { 153 const double coefficient = 0.1; 154 double magnitude = calculateMagnitudePhase(&mPhaseOffset); 155 // One pole averaging filter. 156 setMagnitude((mMagnitude * (1.0 - coefficient)) + (magnitude * coefficient)); 157 return true; 158 } else { 159 return false; 160 } 161 } 162 163 // reset the sine wave detector resetAccumulator()164 virtual void resetAccumulator() { 165 mFramesAccumulated = 0; 166 mSinAccumulator = 0.0; 167 mCosAccumulator = 0.0; 168 } 169 reset()170 void reset() override { 171 LoopbackProcessor::reset(); 172 resetAccumulator(); 173 mMagnitude = 0.0; 174 } 175 prepareToTest()176 void prepareToTest() override { 177 LoopbackProcessor::prepareToTest(); 178 mSinePeriod = getSampleRate() / kTargetGlitchFrequency; 179 mOutputPhase = 0.0f; 180 mInverseSinePeriod = 1.0 / mSinePeriod; 181 mPhaseIncrement = 2.0 * M_PI * mInverseSinePeriod; 182 } 183 184 protected: 185 static constexpr int32_t kTargetGlitchFrequency = 1000; 186 187 int32_t mSinePeriod = 1; // this will be set before use 188 double mInverseSinePeriod = 1.0; 189 double mPhaseIncrement = 0.0; 190 double mOutputPhase = 0.0; 191 double mOutputAmplitude = 0.75; 192 // If this jumps around then we are probably just hearing noise. 193 double mPhaseOffset = 0.0; 194 double mMagnitude = 0.0; 195 int32_t mFramesAccumulated = 0; 196 double mSinAccumulator = 0.0; 197 double mCosAccumulator = 0.0; 198 double mScaledTolerance = 0.0; 199 200 InfiniteRecording<float> mInfiniteRecording; 201 202 private: 203 int32_t mInputChannel = 0; 204 int32_t mOutputChannel = 0; 205 float mTolerance = 0.10; // scaled from 0.0 to 1.0 206 207 float mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC. 208 PseudoRandom mWhiteNoise; 209 }; 210 211 #endif //ANALYZER_BASE_SINE_ANALYZER_H 212