• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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