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() * mNoiseAmplitude); 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 transformSample(float sample,float referencePhase)138 bool transformSample(float sample, float referencePhase) { 139 // Track incoming signal and slowly adjust magnitude to account 140 // for drift in the DRC or AGC. 141 mSinAccumulator += sample * sinf(referencePhase); 142 mCosAccumulator += sample * cosf(referencePhase); 143 mFramesAccumulated++; 144 // Must be a multiple of the period or the calculation will not be accurate. 145 if (mFramesAccumulated == mSinePeriod) { 146 const double coefficient = 0.1; 147 double magnitude = calculateMagnitudePhase(&mPhaseOffset); 148 // One pole averaging filter. 149 setMagnitude((mMagnitude * (1.0 - coefficient)) + (magnitude * coefficient)); 150 return true; 151 } else { 152 return false; 153 } 154 } 155 156 // reset the sine wave detector resetAccumulator()157 virtual void resetAccumulator() { 158 mFramesAccumulated = 0; 159 mSinAccumulator = 0.0; 160 mCosAccumulator = 0.0; 161 } 162 reset()163 void reset() override { 164 LoopbackProcessor::reset(); 165 resetAccumulator(); 166 } 167 prepareToTest()168 void prepareToTest() override { 169 LoopbackProcessor::prepareToTest(); 170 mSinePeriod = getSampleRate() / kTargetGlitchFrequency; 171 mOutputPhase = 0.0f; 172 mInverseSinePeriod = 1.0 / mSinePeriod; 173 mPhaseIncrement = 2.0 * M_PI * mInverseSinePeriod; 174 } 175 176 protected: 177 static constexpr int32_t kTargetGlitchFrequency = 1000; 178 179 int32_t mSinePeriod = 1; // this will be set before use 180 double mInverseSinePeriod = 1.0; 181 double mPhaseIncrement = 0.0; 182 double mOutputPhase = 0.0; 183 double mOutputAmplitude = 0.75; 184 // If this jumps around then we are probably just hearing noise. 185 double mPhaseOffset = 0.0; 186 double mMagnitude = 0.0; 187 int32_t mFramesAccumulated = 0; 188 double mSinAccumulator = 0.0; 189 double mCosAccumulator = 0.0; 190 double mScaledTolerance = 0.0; 191 192 InfiniteRecording<float> mInfiniteRecording; 193 194 private: 195 int32_t mInputChannel = 0; 196 int32_t mOutputChannel = 0; 197 float mTolerance = 0.10; // scaled from 0.0 to 1.0 198 199 float mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC. 200 PseudoRandom mWhiteNoise; 201 }; 202 203 #endif //ANALYZER_BASE_SINE_ANALYZER_H 204