xref: /aosp_15_r20/external/oboe/apps/OboeTester/app/src/main/cpp/analyzer/DataPathAnalyzer.h (revision 05767d913155b055644481607e6fa1e35e2fe72c)
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_DATA_PATH_ANALYZER_H
18 #define ANALYZER_DATA_PATH_ANALYZER_H
19 
20 #include <algorithm>
21 #include <cctype>
22 #include <iomanip>
23 #include <iostream>
24 #include <math.h>
25 
26 #include "BaseSineAnalyzer.h"
27 #include "InfiniteRecording.h"
28 #include "LatencyAnalyzer.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.
35  */
36 class DataPathAnalyzer : public BaseSineAnalyzer {
37 public:
38 
DataPathAnalyzer()39     DataPathAnalyzer() : BaseSineAnalyzer() {
40         // Add a little bit of noise to reduce blockage by speaker protection and DRC.
41         setNoiseAmplitude(0.02);
42     }
43 
calculatePhaseError(double p1,double p2)44     double calculatePhaseError(double p1, double p2) {
45         double diff = p1 - p2;
46         // Wrap around the circle.
47         while (diff > M_PI) {
48             diff -= (2 * M_PI);
49         }
50         while (diff < -M_PI) {
51             diff += (2 * M_PI);
52         }
53         return diff;
54     }
55 
56     /**
57      * @param frameData contains microphone data with sine signal feedback
58      * @param channelCount
59      */
processInputFrame(const float * frameData,int)60     result_code processInputFrame(const float *frameData, int /* channelCount */) override {
61         result_code result = RESULT_OK;
62 
63         float sample = frameData[getInputChannel()];
64         mInfiniteRecording.write(sample);
65 
66         if (transformSample(sample, mOutputPhase)) {
67             // Analyze magnitude and phase on every period.
68             if (mPhaseOffset != kPhaseInvalid) {
69                 double diff = fabs(calculatePhaseError(mPhaseOffset, mPreviousPhaseOffset));
70                 if (diff < mPhaseTolerance) {
71                     mMaxMagnitude = std::max(mMagnitude, mMaxMagnitude);
72                 }
73                 mPreviousPhaseOffset = mPhaseOffset;
74             }
75         }
76         return result;
77     }
78 
analyze()79     std::string analyze() override {
80         std::stringstream report;
81         report << "DataPathAnalyzer ------------------\n";
82         report << LOOPBACK_RESULT_TAG "sine.magnitude     = " << std::setw(8)
83                << mMagnitude << "\n";
84         report << LOOPBACK_RESULT_TAG "frames.accumulated = " << std::setw(8)
85                << mFramesAccumulated << "\n";
86         report << LOOPBACK_RESULT_TAG "sine.period        = " << std::setw(8)
87                << mSinePeriod << "\n";
88         return report.str();
89     }
90 
reset()91     void reset() override {
92         BaseSineAnalyzer::reset();
93         mPreviousPhaseOffset = 999.0; // Arbitrary high offset to prevent early lock.
94         mMaxMagnitude = 0.0;
95     }
96 
getMaxMagnitude()97     double getMaxMagnitude() {
98         return mMaxMagnitude;
99     }
100 
101 private:
102     double  mPreviousPhaseOffset = 0.0;
103     double  mPhaseTolerance = 2 * M_PI  / 48;
104     double  mMaxMagnitude = 0.0;
105 };
106 #endif // ANALYZER_DATA_PATH_ANALYZER_H
107