Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5bce4c9f authored by Phil Burk's avatar Phil Burk
Browse files

aaudio_loopback: improve tolerance to noise

Make silence detector less sensitive to noise by setting the
threshold proportional to the measured pulse level.

Make glitch detector less sensitive to noise by setting the
threshold proportional to the measured noise level.

Report signal to noise ratio.

Improved printed messages, show state as text.

Bug: 117834966
Test: aaudio_loopback -pl -tm
Change-Id: I5593cd1ade93ecbcb05f42af07626013d036f29a
parent 69c9844b
Loading
Loading
Loading
Loading
+142 −66
Original line number Diff line number Diff line
@@ -163,8 +163,7 @@ static int measureLatencyFromEchos(const float *haystack, int haystackSize,
                            const float *needle, int needleSize,
                            LatencyReport *report) {
    const double threshold = 0.1;
    printf("measureLatencyFromEchos: haystackSize = %d, needleSize = %d\n",
           haystackSize, needleSize);
    // printf("%s: haystackSize = %d, needleSize = %d\n", __func__, haystackSize, needleSize);

    // Find first peak
    int first = (int) (findFirstMatch(haystack,
@@ -181,8 +180,6 @@ static int measureLatencyFromEchos(const float *haystack, int haystackSize,
                                      needle,
                                      needleSize,
                                      threshold) + 0.5);

    printf("measureLatencyFromEchos: first = %d, again at %d\n", first, again);
    first = again;

    // Allocate results array
@@ -199,7 +196,7 @@ static int measureLatencyFromEchos(const float *haystack, int haystackSize,

    // Add higher harmonics mapped onto lower harmonics.
    // This reinforces the "fundamental" echo.
    const int numEchoes = 10;
    const int numEchoes = 8;
    for (int partial = 1; partial < numEchoes; partial++) {
        for (int i = 0; i < numCorrelations; i++) {
            harmonicSums[i / partial] += correlations[i] / partial;
@@ -216,7 +213,7 @@ static int measureLatencyFromEchos(const float *haystack, int haystackSize,
            maxCorrelation = harmonicSums[i];
            sumOfPeaks += maxCorrelation;
            peakIndex = i;
            printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
            // printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
        }
    }

@@ -476,9 +473,13 @@ public:
    void report() override {

        printf("EchoAnalyzer ---------------\n");
        printf(LOOPBACK_RESULT_TAG "measured.gain          = %f\n", mMeasuredLoopGain);
        printf(LOOPBACK_RESULT_TAG "echo.gain              = %f\n", mEchoGain);
        printf(LOOPBACK_RESULT_TAG "test.state             = %d\n", mState);
        printf(LOOPBACK_RESULT_TAG "measured.gain          = %8f\n", mMeasuredLoopGain);
        printf(LOOPBACK_RESULT_TAG "echo.gain              = %8f\n", mEchoGain);
        printf(LOOPBACK_RESULT_TAG "test.state             = %8d\n", mState);
        printf(LOOPBACK_RESULT_TAG "test.state.name        = %8s\n", convertStateToText(mState));
        if (mState == STATE_WAITING_FOR_SILENCE) {
            printf("WARNING - Stuck waiting for silence. Input may be too noisy!\n");
        }
        if (mMeasuredLoopGain >= 0.9999) {
            printf("   ERROR - clipping, turn down volume slightly\n");
        } else {
@@ -491,9 +492,12 @@ public:
                printf("   ERROR - confidence too low = %f\n", mLatencyReport.confidence);
            } else {
                double latencyMillis = 1000.0 * mLatencyReport.latencyInFrames / getSampleRate();
                printf(LOOPBACK_RESULT_TAG "latency.frames        = %8.2f\n", mLatencyReport.latencyInFrames);
                printf(LOOPBACK_RESULT_TAG "latency.msec          = %8.2f\n", latencyMillis);
                printf(LOOPBACK_RESULT_TAG "latency.confidence    = %8.6f\n", mLatencyReport.confidence);
                printf(LOOPBACK_RESULT_TAG "latency.frames         = %8.2f\n",
                       mLatencyReport.latencyInFrames);
                printf(LOOPBACK_RESULT_TAG "latency.msec           = %8.2f\n",
                       latencyMillis);
                printf(LOOPBACK_RESULT_TAG "latency.confidence     = %8.6f\n",
                       mLatencyReport.confidence);
            }
        }
    }
@@ -527,7 +531,7 @@ public:
        int numWritten;
        int numSamples;

        echo_state_t nextState = mState;
        echo_state nextState = mState;

        switch (mState) {
            case STATE_INITIAL_SILENCE:
@@ -553,6 +557,7 @@ public:
                        //       mLoopCounter, peak);
                        mDownCounter = 8;
                        mMeasuredLoopGain = peak;  // assumes original pulse amplitude is one
                        mSilenceThreshold = peak * 0.1; // scale silence to measured pulse
                        // Calculate gain that will give us a nice decaying echo.
                        mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
                        if (mEchoGain > MAX_ECHO_GAIN) {
@@ -638,7 +643,7 @@ public:

private:

    enum echo_state_t {
    enum echo_state {
        STATE_INITIAL_SILENCE,
        STATE_MEASURING_GAIN,
        STATE_WAITING_FOR_SILENCE,
@@ -648,6 +653,35 @@ private:
        STATE_FAILED
    };

    const char *convertStateToText(echo_state state) {
        const char *result = "Unknown";
        switch(state) {
            case STATE_INITIAL_SILENCE:
                result = "INIT";
                break;
            case STATE_MEASURING_GAIN:
                result = "GAIN";
                break;
            case STATE_WAITING_FOR_SILENCE:
                result = "SILENCE";
                break;
            case STATE_SENDING_PULSE:
                result = "PULSE";
                break;
            case STATE_GATHERING_ECHOS:
                result = "ECHOS";
                break;
            case STATE_DONE:
                result = "DONE";
                break;
            case STATE_FAILED:
                result = "FAILED";
                break;
        }
        return result;
    }


    int32_t         mDownCounter = 500;
    int32_t         mLoopCounter = 0;
    int32_t         mSampleIndex = 0;
@@ -656,7 +690,7 @@ private:
    float           mMeasuredLoopGain = 0.0f;
    float           mDesiredEchoGain = 0.95f;
    float           mEchoGain = 1.0f;
    echo_state_t    mState = STATE_INITIAL_SILENCE;
    echo_state      mState = STATE_INITIAL_SILENCE;

    AudioRecording  mAudioRecording; // contains only the input after the gain detection burst
    LatencyReport   mLatencyReport;
@@ -680,21 +714,32 @@ public:

    void report() override {
        printf("SineAnalyzer ------------------\n");
        printf(LOOPBACK_RESULT_TAG "peak.amplitude     = %7.5f\n", mPeakAmplitude);
        printf(LOOPBACK_RESULT_TAG "sine.magnitude     = %7.5f\n", mMagnitude);
        printf(LOOPBACK_RESULT_TAG "phase.offset       = %7.5f\n", mPhaseOffset);
        printf(LOOPBACK_RESULT_TAG "ref.phase          = %7.5f\n", mPhase);
        printf(LOOPBACK_RESULT_TAG "frames.accumulated = %6d\n", mFramesAccumulated);
        printf(LOOPBACK_RESULT_TAG "sine.period        = %6d\n", mSinePeriod);
        printf(LOOPBACK_RESULT_TAG "test.state         = %6d\n", mState);
        printf(LOOPBACK_RESULT_TAG "frame.count        = %6d\n", mFrameCounter);
        printf(LOOPBACK_RESULT_TAG "peak.amplitude     = %8f\n", mPeakAmplitude);
        printf(LOOPBACK_RESULT_TAG "sine.magnitude     = %8f\n", mMagnitude);
        printf(LOOPBACK_RESULT_TAG "peak.noise         = %8f\n", mPeakNoise);
        printf(LOOPBACK_RESULT_TAG "rms.noise          = %8f\n", mRootMeanSquareNoise);
        float amplitudeRatio = mMagnitude / mPeakNoise;
        float signalToNoise = amplitudeRatio * amplitudeRatio;
        printf(LOOPBACK_RESULT_TAG "signal.to.noise    = %8.2f\n", signalToNoise);
        float signalToNoiseDB = 10.0 * log(signalToNoise);
        printf(LOOPBACK_RESULT_TAG "signal.to.noise.db = %8.2f\n", signalToNoiseDB);
        if (signalToNoiseDB < MIN_SNRATIO_DB) {
            printf("WARNING - signal to noise ratio is too low! < %d dB\n", MIN_SNRATIO_DB);
        }
        printf(LOOPBACK_RESULT_TAG "phase.offset       = %8.5f\n", mPhaseOffset);
        printf(LOOPBACK_RESULT_TAG "ref.phase          = %8.5f\n", mPhase);
        printf(LOOPBACK_RESULT_TAG "frames.accumulated = %8d\n", mFramesAccumulated);
        printf(LOOPBACK_RESULT_TAG "sine.period        = %8d\n", mSinePeriod);
        printf(LOOPBACK_RESULT_TAG "test.state         = %8d\n", mState);
        printf(LOOPBACK_RESULT_TAG "frame.count        = %8d\n", mFrameCounter);
        // Did we ever get a lock?
        bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0);
        if (!gotLock) {
            printf("ERROR - failed to lock on reference sine tone\n");
        } else {
            // Only print if meaningful.
            printf(LOOPBACK_RESULT_TAG "glitch.count       = %6d\n", mGlitchCount);
            printf(LOOPBACK_RESULT_TAG "glitch.count       = %8d\n", mGlitchCount);
            printf(LOOPBACK_RESULT_TAG "max.glitch         = %8f\n", mMaxGlitchDelta);
        }
    }

@@ -732,15 +777,48 @@ public:
        }

        for (int i = 0; i < numFrames; i++) {
            bool sineEnabled = true;
            float sample = inputData[i * inputChannelCount];

            float sinOut = sinf(mPhase);

            switch (mState) {
                case STATE_IDLE:
                    sineEnabled = false;
                    mDownCounter--;
                    if (mDownCounter <= 0) {
                        mState = STATE_MEASURE_NOISE;
                        mDownCounter = NOISE_FRAME_COUNT;
                    }
                    break;
                case STATE_MEASURE_NOISE:
                    sineEnabled = false;
                    mPeakNoise = std::max(abs(sample), mPeakNoise);
                    mNoiseSumSquared += sample * sample;
                    mDownCounter--;
                    if (mDownCounter <= 0) {
                        mState = STATE_WAITING_FOR_SIGNAL;
                        mRootMeanSquareNoise = sqrt(mNoiseSumSquared / NOISE_FRAME_COUNT);
                        mTolerance = std::max(MIN_TOLERANCE, mPeakNoise * 2.0f);
                        mPhase = 0.0; // prevent spike at start
                    }
                    break;

                case STATE_IMMUNE:
                    mDownCounter--;
                    if (mDownCounter <= 0) {
                        mState = STATE_WAITING_FOR_SIGNAL;
                    }
                    break;

                case STATE_WAITING_FOR_SIGNAL:
                    if (peak > mThreshold) {
                        mState = STATE_WAITING_FOR_LOCK;
                        //printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter);
                        resetAccumulator();
                    }
                    break;

                case STATE_WAITING_FOR_LOCK:
                    mSinAccumulator += sample * sinOut;
                    mCosAccumulator += sample * cosf(mPhase);
@@ -766,13 +844,14 @@ public:
                    // printf("    predicted = %f, actual = %f\n", predicted, sample);

                    float diff = predicted - sample;
                    if (fabs(diff) > mTolerance) {
                    float absDiff = fabs(diff);
                    mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff);
                    if (absDiff > mTolerance) {
                        mGlitchCount++;
                        //printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n",
                        //       mFrameCounter, mGlitchCount, predicted, sample);
                        mState = STATE_IMMUNE;
                        //printf("%5d: switch to STATE_IMMUNE\n", mFrameCounter);
                        mDownCounter = mSinePeriod;  // Set duration of IMMUNE state.
                        mDownCounter = mSinePeriod * PERIODS_IMMUNE;
                    }

                    // Track incoming signal and slowly adjust magnitude to account
@@ -792,44 +871,23 @@ public:
                } break;
            }

            float output = 0.0f;
            // Output sine wave so we can measure it.
            outputData[i * outputChannelCount] = (sinOut * mOutputAmplitude)
            if (sineEnabled) {
                output = (sinOut * mOutputAmplitude)
                         + (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude);
                // printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut,  mPhaseIncrement);

                // advance and wrap phase
                mPhase += mPhaseIncrement;
                if (mPhase > M_PI) {
                    mPhase -= (2.0 * M_PI);
                }

            mFrameCounter++;
            }
            outputData[i * outputChannelCount] = output;

        // Do these once per buffer.
        switch (mState) {
            case STATE_IDLE:
                mState = STATE_IMMUNE; // so we can tell when
                break;
            case STATE_IMMUNE:
                mDownCounter -= numFrames;
                if (mDownCounter <= 0) {
                    mState = STATE_WAITING_FOR_SIGNAL;
                    //printf("%5d: switch to STATE_WAITING_FOR_SIGNAL\n", mFrameCounter);
                }
                break;
            case STATE_WAITING_FOR_SIGNAL:
                if (peak > mThreshold) {
                    mState = STATE_WAITING_FOR_LOCK;
                    //printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter);
                    resetAccumulator();
                }
                break;
            case STATE_WAITING_FOR_LOCK:
            case STATE_LOCKED:
                break;
        }

            mFrameCounter++;
        }
    }

    void resetAccumulator() {
@@ -840,18 +898,24 @@ public:

    void reset() override {
        mGlitchCount = 0;
        mState = STATE_IMMUNE;
        mDownCounter = IMMUNE_FRAME_COUNT;
        mState = STATE_IDLE;
        mDownCounter = IDLE_FRAME_COUNT;
        mPhaseIncrement = 2.0 * M_PI / mSinePeriod;
        printf("phaseInc = %f for period %d\n", mPhaseIncrement, mSinePeriod);
        resetAccumulator();
        mProcessCount = 0;
        mPeakNoise = 0.0f;
        mNoiseSumSquared = 0.0;
        mRootMeanSquareNoise = 0.0;
        mPhase = 0.0f;
        mMaxGlitchDelta = 0.0;
    }

private:

    enum sine_state_t {
        STATE_IDLE,
        STATE_MEASURE_NOISE,
        STATE_IMMUNE,
        STATE_WAITING_FOR_SIGNAL,
        STATE_WAITING_FOR_LOCK,
@@ -859,10 +923,16 @@ private:
    };

    enum constants {
        IMMUNE_FRAME_COUNT = 48 * 500,
        PERIODS_NEEDED_FOR_LOCK = 8
        // Arbitrary durations, assuming 48000 Hz
        IDLE_FRAME_COUNT = 48 * 100,
        NOISE_FRAME_COUNT = 48 * 600,
        PERIODS_NEEDED_FOR_LOCK = 8,
        PERIODS_IMMUNE = 2,
        MIN_SNRATIO_DB = 65
    };

    static constexpr float MIN_TOLERANCE = 0.01;

    int     mSinePeriod = 79;
    double  mPhaseIncrement = 0.0;
    double  mPhase = 0.0;
@@ -870,17 +940,23 @@ private:
    double  mPreviousPhaseOffset = 0.0;
    double  mMagnitude = 0.0;
    double  mThreshold = 0.005;
    double  mTolerance = 0.01;
    double  mTolerance = MIN_TOLERANCE;
    int32_t mFramesAccumulated = 0;
    int32_t mProcessCount = 0;
    double  mSinAccumulator = 0.0;
    double  mCosAccumulator = 0.0;
    float   mMaxGlitchDelta = 0.0f;
    int32_t mGlitchCount = 0;
    double  mPeakAmplitude = 0.0;
    int     mDownCounter = IMMUNE_FRAME_COUNT;
    int     mDownCounter = IDLE_FRAME_COUNT;
    int32_t mFrameCounter = 0;
    float   mOutputAmplitude = 0.75;

    // measure background noise
    float   mPeakNoise = 0.0f;
    double  mNoiseSumSquared = 0.0;
    double  mRootMeanSquareNoise = 0.0;

    PseudoRandom  mWhiteNoise;
    float   mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC.

+16 −20
Original line number Diff line number Diff line
@@ -37,13 +37,12 @@

// Tag for machine readable results as property = value pairs
#define RESULT_TAG              "RESULT: "
#define NUM_SECONDS             5
#define PERIOD_MILLIS           1000
#define NUM_INPUT_CHANNELS      1
#define FILENAME_ALL            "/data/loopback_all.wav"
#define FILENAME_ECHOS          "/data/loopback_echos.wav"
#define APP_VERSION             "0.2.04"
#define APP_VERSION             "0.3.00"

constexpr int kLogPeriodMillis       = 1000;
constexpr int kNumInputChannels      = 1;
constexpr int kNumCallbacksToDrain   = 20;
constexpr int kNumCallbacksToDiscard = 20;

@@ -336,7 +335,7 @@ int main(int argc, const char **argv)

    aaudio_result_t       result = AAUDIO_OK;
    aaudio_sharing_mode_t requestedInputSharingMode  = AAUDIO_SHARING_MODE_SHARED;
    int                   requestedInputChannelCount = NUM_INPUT_CHANNELS;
    int                   requestedInputChannelCount = kNumInputChannels;
    aaudio_format_t       requestedInputFormat       = AAUDIO_FORMAT_UNSPECIFIED;
    int32_t               requestedInputCapacity     = AAUDIO_UNSPECIFIED;
    aaudio_performance_mode_t inputPerformanceLevel  = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
@@ -459,7 +458,9 @@ int main(int argc, const char **argv)
    argParser.setPerformanceMode(inputPerformanceLevel);
    argParser.setChannelCount(requestedInputChannelCount);
    argParser.setSharingMode(requestedInputSharingMode);
    // Warning! If you change input capacity then you may not get a FAST track on Legacy path.
    if (requestedInputCapacity != AAUDIO_UNSPECIFIED) {
        printf("Warning! If you set input capacity then maybe no FAST track on Legacy path!\n");
    }
    argParser.setBufferCapacity(requestedInputCapacity);

    result = recorder.open(argParser);
@@ -510,15 +511,11 @@ int main(int argc, const char **argv)
    // Start OUTPUT first so INPUT does not overflow.
    result = player.start();
    if (result != AAUDIO_OK) {
        printf("ERROR - AAudioStream_requestStart(output) returned %d = %s\n",
               result, AAudio_convertResultToText(result));
        goto finish;
    }

    result = recorder.start();
    if (result != AAUDIO_OK) {
        printf("ERROR - AAudioStream_requestStart(input) returned %d = %s\n",
               result, AAudio_convertResultToText(result));
        goto finish;
    }

@@ -561,7 +558,7 @@ int main(int argc, const char **argv)
                   AAudioStream_getXRunCount(outputStream)
            );
        }
        int32_t periodMillis = (timeMillis < 2000) ? PERIOD_MILLIS / 4 : PERIOD_MILLIS;
        int32_t periodMillis = (timeMillis < 2000) ? kLogPeriodMillis / 4 : kLogPeriodMillis;
        usleep(periodMillis * 1000);
        timeMillis += periodMillis;
    }
@@ -586,12 +583,12 @@ int main(int argc, const char **argv)
    if (loopbackData.inputError == AAUDIO_OK) {
        if (testMode == TEST_SINE_MAGNITUDE) {
            printAudioGraph(loopbackData.audioRecording, 200);
        }
            // Print again so we don't have to scroll past waveform.
            printf("OUTPUT Stream ----------------------------------------\n");
            argParser.compareWithStream(outputStream);
            printf("INPUT  Stream ----------------------------------------\n");
            argParser.compareWithStream(inputStream);
        }

        loopbackData.loopbackProcessor->report();
    }
@@ -600,21 +597,21 @@ int main(int argc, const char **argv)
        int32_t framesRead = AAudioStream_getFramesRead(inputStream);
        int32_t framesWritten = AAudioStream_getFramesWritten(inputStream);
        printf("Callback Results ---------------------------------------- INPUT\n");
        printf("  input overruns   = %d\n", AAudioStream_getXRunCount(inputStream));
        printf("  input overruns   = %8d\n", AAudioStream_getXRunCount(inputStream));
        printf("  framesWritten    = %8d\n", framesWritten);
        printf("  framesRead       = %8d\n", framesRead);
        printf("  myFramesRead     = %8d\n", (int) loopbackData.framesReadTotal);
        printf("  written - read   = %8d\n", (int) (framesWritten - framesRead));
        printf("  insufficient #   = %8d\n", (int) loopbackData.insufficientReadCount);
        if (loopbackData.insufficientReadCount > 0) {
            printf("  insufficient frames = %8d\n", (int) loopbackData.insufficientReadFrames);
            printf("  insuffic. frames = %8d\n", (int) loopbackData.insufficientReadFrames);
        }
    }
    {
        int32_t framesRead = AAudioStream_getFramesRead(outputStream);
        int32_t framesWritten = AAudioStream_getFramesWritten(outputStream);
        printf("Callback Results ---------------------------------------- OUTPUT\n");
        printf("  output underruns = %d\n", AAudioStream_getXRunCount(outputStream));
        printf("  output underruns = %8d\n", AAudioStream_getXRunCount(outputStream));
        printf("  myFramesWritten  = %8d\n", (int) loopbackData.framesWrittenTotal);
        printf("  framesWritten    = %8d\n", framesWritten);
        printf("  framesRead       = %8d\n", framesRead);
@@ -659,4 +656,3 @@ finish:
        return EXIT_SUCCESS;
    }
}
+4 −4
Original line number Diff line number Diff line
@@ -193,7 +193,7 @@ public:
     aaudio_result_t start() {
        aaudio_result_t result = AAudioStream_requestStart(mStream);
        if (result != AAUDIO_OK) {
            printf("ERROR - AAudioStream_requestStart() returned %d %s\n",
            printf("ERROR - AAudioStream_requestStart(output) returned %d %s\n",
                    result, AAudio_convertResultToText(result));
        }
        return result;
@@ -203,7 +203,7 @@ public:
    aaudio_result_t stop() {
        aaudio_result_t result = AAudioStream_requestStop(mStream);
        if (result != AAUDIO_OK) {
            printf("ERROR - AAudioStream_requestStop() returned %d %s\n",
            printf("ERROR - AAudioStream_requestStop(output) returned %d %s\n",
                   result, AAudio_convertResultToText(result));
        }
        int32_t xRunCount = AAudioStream_getXRunCount(mStream);
@@ -215,7 +215,7 @@ public:
    aaudio_result_t pause() {
        aaudio_result_t result = AAudioStream_requestPause(mStream);
        if (result != AAUDIO_OK) {
            printf("ERROR - AAudioStream_requestPause() returned %d %s\n",
            printf("ERROR - AAudioStream_requestPause(output) returned %d %s\n",
                   result, AAudio_convertResultToText(result));
        }
        int32_t xRunCount = AAudioStream_getXRunCount(mStream);
@@ -227,7 +227,7 @@ public:
    aaudio_result_t flush() {
        aaudio_result_t result = AAudioStream_requestFlush(mStream);
        if (result != AAUDIO_OK) {
            printf("ERROR - AAudioStream_requestFlush() returned %d %s\n",
            printf("ERROR - AAudioStream_requestFlush(output) returned %d %s\n",
                   result, AAudio_convertResultToText(result));
        }
        return result;
+6 −3
Original line number Diff line number Diff line
@@ -201,8 +201,10 @@ public:
     aaudio_result_t start() {
        aaudio_result_t result = AAudioStream_requestStart(mStream);
        if (result != AAUDIO_OK) {
            fprintf(stderr, "ERROR - AAudioStream_requestStart() returned %d %s\n",
            fprintf(stderr, "ERROR - AAudioStream_requestStart(input) returned %d %s\n",
                    result, AAudio_convertResultToText(result));
            fprintf(stderr, "        Did you remember to enter:   adb root    ????\n");

        }
        return result;
    }
@@ -211,8 +213,9 @@ public:
    aaudio_result_t stop() {
        aaudio_result_t result = AAudioStream_requestStop(mStream);
        if (result != AAUDIO_OK) {
            fprintf(stderr, "ERROR - AAudioStream_requestStop() returned %d %s\n",
            fprintf(stderr, "ERROR - AAudioStream_requestStop(input) returned %d %s\n",
                    result, AAudio_convertResultToText(result));

        }
        return result;
    }
@@ -221,7 +224,7 @@ public:
    aaudio_result_t pause() {
        aaudio_result_t result = AAudioStream_requestPause(mStream);
        if (result != AAUDIO_OK) {
            fprintf(stderr, "ERROR - AAudioStream_requestPause() returned %d %s\n",
            fprintf(stderr, "ERROR - AAudioStream_requestPause(input) returned %d %s\n",
                    result, AAudio_convertResultToText(result));
        }
        return result;