aboutsummaryrefslogtreecommitdiff
path: root/apps/OboeTester/app/src/main/cpp/analyzer/DataPathAnalyzer.h
blob: 953cf49ab118fd242c371ba183492f187cb1ab04 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANALYZER_DATA_PATH_ANALYZER_H
#define ANALYZER_DATA_PATH_ANALYZER_H

#include <algorithm>
#include <cctype>
#include <iomanip>
#include <iostream>
#include <math.h>

#include "BaseSineAnalyzer.h"
#include "InfiniteRecording.h"
#include "LatencyAnalyzer.h"

/**
 * Output a steady sine wave and analyze the return signal.
 *
 * Use a cosine transform to measure the predicted magnitude and relative phase of the
 * looped back sine wave.
 */
class DataPathAnalyzer : public BaseSineAnalyzer {
public:

    DataPathAnalyzer() : BaseSineAnalyzer() {
        // Add a little bit of noise to reduce blockage by speaker protection and DRC.
        setNoiseAmplitude(0.02);
    }

    double calculatePhaseError(double p1, double p2) {
        double diff = p1 - p2;
        // Wrap around the circle.
        while (diff > M_PI) {
            diff -= (2 * M_PI);
        }
        while (diff < -M_PI) {
            diff += (2 * M_PI);
        }
        return diff;
    }

    /**
     * @param frameData contains microphone data with sine signal feedback
     * @param channelCount
     */
    result_code processInputFrame(const float *frameData, int /* channelCount */) override {
        result_code result = RESULT_OK;

        float sample = frameData[getInputChannel()];
        mInfiniteRecording.write(sample);

        if (transformSample(sample, mOutputPhase)) {
            // Analyze magnitude and phase on every period.
            double diff = fabs(calculatePhaseError(mPhaseOffset, mPreviousPhaseOffset));
            if (diff < mPhaseTolerance) {
                mMaxMagnitude = std::max(mMagnitude, mMaxMagnitude);
            }
            mPreviousPhaseOffset = mPhaseOffset;
        }
        return result;
    }

    std::string analyze() override {
        std::stringstream report;
        report << "DataPathAnalyzer ------------------\n";
        report << LOOPBACK_RESULT_TAG "sine.magnitude     = " << std::setw(8)
               << mMagnitude << "\n";
        report << LOOPBACK_RESULT_TAG "frames.accumulated = " << std::setw(8)
               << mFramesAccumulated << "\n";
        report << LOOPBACK_RESULT_TAG "sine.period        = " << std::setw(8)
               << mSinePeriod << "\n";
        return report.str();
    }

    void reset() override {
        BaseSineAnalyzer::reset();
        mPreviousPhaseOffset = 999.0; // Arbitrary high offset to prevent early lock.
        mMaxMagnitude = 0.0;
    }

    double getMaxMagnitude() {
        return mMaxMagnitude;
    }

private:
    double  mPreviousPhaseOffset = 0.0;
    double  mPhaseTolerance = 2 * M_PI  / 48;
    double  mMaxMagnitude = 0.0;
};
#endif // ANALYZER_DATA_PATH_ANALYZER_H