aboutsummaryrefslogtreecommitdiff
path: root/externals/oboe/apps/OboeTester/app/src/main/cpp/analyzer/LatencyAnalyzer.h
diff options
context:
space:
mode:
Diffstat (limited to 'externals/oboe/apps/OboeTester/app/src/main/cpp/analyzer/LatencyAnalyzer.h')
-rw-r--r--externals/oboe/apps/OboeTester/app/src/main/cpp/analyzer/LatencyAnalyzer.h862
1 files changed, 0 insertions, 862 deletions
diff --git a/externals/oboe/apps/OboeTester/app/src/main/cpp/analyzer/LatencyAnalyzer.h b/externals/oboe/apps/OboeTester/app/src/main/cpp/analyzer/LatencyAnalyzer.h
deleted file mode 100644
index 165ba140fe..0000000000
--- a/externals/oboe/apps/OboeTester/app/src/main/cpp/analyzer/LatencyAnalyzer.h
+++ /dev/null
@@ -1,862 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-/**
- * Tools for measuring latency and for detecting glitches.
- * These classes are pure math and can be used with any audio system.
- */
-
-#ifndef ANALYZER_LATENCY_ANALYZER_H
-#define ANALYZER_LATENCY_ANALYZER_H
-
-#include <algorithm>
-#include <assert.h>
-#include <cctype>
-#include <iomanip>
-#include <iostream>
-#include <math.h>
-#include <memory>
-#include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <vector>
-
-#include "PeakDetector.h"
-#include "PseudoRandom.h"
-#include "RandomPulseGenerator.h"
-
-// This is used when the code is in not in Android.
-#ifndef ALOGD
-#define ALOGD LOGD
-#define ALOGE LOGE
-#define ALOGW LOGW
-#endif
-
-#define LOOPBACK_RESULT_TAG "RESULT: "
-
-// Enable or disable the optimized latency calculation.
-#define USE_FAST_LATENCY_CALCULATION 1
-
-static constexpr int32_t kDefaultSampleRate = 48000;
-static constexpr int32_t kMillisPerSecond = 1000; // by definition
-static constexpr int32_t kMaxLatencyMillis = 1000; // arbitrary and generous
-
-struct LatencyReport {
- int32_t latencyInFrames = 0;
- double correlation = 0.0;
-
- void reset() {
- latencyInFrames = 0;
- correlation = 0.0;
- }
-};
-
-/**
- * Calculate a normalized cross correlation.
- * @return value between -1.0 and 1.0
- */
-
-static float calculateNormalizedCorrelation(const float *a,
- const float *b,
- int windowSize,
- int stride) {
- float correlation = 0.0;
- float sumProducts = 0.0;
- float sumSquares = 0.0;
-
- // Correlate a against b.
- for (int i = 0; i < windowSize; i += stride) {
- float s1 = a[i];
- float s2 = b[i];
- // Use a normalized cross-correlation.
- sumProducts += s1 * s2;
- sumSquares += ((s1 * s1) + (s2 * s2));
- }
-
- if (sumSquares >= 1.0e-9) {
- correlation = 2.0 * sumProducts / sumSquares;
- }
- return correlation;
-}
-
-static double calculateRootMeanSquare(float *data, int32_t numSamples) {
- double sum = 0.0;
- for (int32_t i = 0; i < numSamples; i++) {
- double sample = data[i];
- sum += sample * sample;
- }
- return sqrt(sum / numSamples);
-}
-
-/**
- * Monophonic recording with processing.
- * Samples are stored as floats internally.
- */
-class AudioRecording
-{
-public:
-
- void allocate(int maxFrames) {
- mData = std::make_unique<float[]>(maxFrames);
- mMaxFrames = maxFrames;
- mFrameCounter = 0;
- }
-
- // Write SHORT data from the first channel.
- int32_t write(const int16_t *inputData, int32_t inputChannelCount, int32_t numFrames) {
- // stop at end of buffer
- if ((mFrameCounter + numFrames) > mMaxFrames) {
- numFrames = mMaxFrames - mFrameCounter;
- }
- for (int i = 0; i < numFrames; i++) {
- mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768);
- }
- return numFrames;
- }
-
- // Write FLOAT data from the first channel.
- int32_t write(const float *inputData, int32_t inputChannelCount, int32_t numFrames) {
- // stop at end of buffer
- if ((mFrameCounter + numFrames) > mMaxFrames) {
- numFrames = mMaxFrames - mFrameCounter;
- }
- for (int i = 0; i < numFrames; i++) {
- mData[mFrameCounter++] = inputData[i * inputChannelCount];
- }
- return numFrames;
- }
-
- // Write single FLOAT value.
- int32_t write(float sample) {
- // stop at end of buffer
- if (mFrameCounter < mMaxFrames) {
- mData[mFrameCounter++] = sample;
- return 1;
- }
- return 0;
- }
-
- void clear() {
- mFrameCounter = 0;
- }
-
- int32_t size() const {
- return mFrameCounter;
- }
-
- bool isFull() const {
- return mFrameCounter >= mMaxFrames;
- }
-
- float *getData() const {
- return mData.get();
- }
-
- void setSampleRate(int32_t sampleRate) {
- mSampleRate = sampleRate;
- }
-
- int32_t getSampleRate() const {
- return mSampleRate;
- }
-
- /**
- * Square the samples so they are all positive and so the peaks are emphasized.
- */
- void square() {
- float *x = mData.get();
- for (int i = 0; i < mFrameCounter; i++) {
- x[i] *= x[i];
- }
- }
-
- // Envelope follower that rides over the peak values.
- void detectPeaks(float decay) {
- float level = 0.0f;
- float *x = mData.get();
- for (int i = 0; i < mFrameCounter; i++) {
- level *= decay; // exponential decay
- float input = fabs(x[i]);
- // never fall below the input signal
- if (input > level) {
- level = input;
- }
- x[i] = level; // write result back into the array
- }
- }
-
- /**
- * Amplify a signal so that the peak matches the specified target.
- *
- * @param target final max value
- * @return gain applied to signal
- */
- float normalize(float target) {
- float maxValue = 1.0e-9f;
- for (int i = 0; i < mFrameCounter; i++) {
- maxValue = std::max(maxValue, fabsf(mData[i]));
- }
- float gain = target / maxValue;
- for (int i = 0; i < mFrameCounter; i++) {
- mData[i] *= gain;
- }
- return gain;
- }
-
-private:
- std::unique_ptr<float[]> mData;
- int32_t mFrameCounter = 0;
- int32_t mMaxFrames = 0;
- int32_t mSampleRate = kDefaultSampleRate; // common default
-};
-
-/**
- * Find latency using cross correlation in window of the recorded audio.
- * The stride is used to skip over samples and reduce the CPU load.
- */
-static int measureLatencyFromPulsePartial(AudioRecording &recorded,
- int32_t recordedOffset,
- int32_t recordedWindowSize,
- AudioRecording &pulse,
- LatencyReport *report,
- int32_t stride) {
- report->reset();
-
- if (recordedOffset + recordedWindowSize + pulse.size() > recorded.size()) {
- ALOGE("%s() tried to correlate past end of recording, recordedOffset = %d frames\n",
- __func__, recordedOffset);
- return -3;
- }
-
- int32_t numCorrelations = recordedWindowSize / stride;
- if (numCorrelations < 10) {
- ALOGE("%s() recording too small = %d frames, numCorrelations = %d\n",
- __func__, recorded.size(), numCorrelations);
- return -1;
- }
- std::unique_ptr<float[]> correlations= std::make_unique<float[]>(numCorrelations);
-
- // Correlate pulse against the recorded data.
- for (int32_t i = 0; i < numCorrelations; i++) {
- const int32_t index = (i * stride) + recordedOffset;
- float correlation = calculateNormalizedCorrelation(&recorded.getData()[index],
- &pulse.getData()[0],
- pulse.size(),
- stride);
- correlations[i] = correlation;
- }
- // Find highest peak in correlation array.
- float peakCorrelation = 0.0;
- int32_t peakIndex = -1;
- for (int32_t i = 0; i < numCorrelations; i++) {
- float value = fabsf(correlations[i]);
- if (value > peakCorrelation) {
- peakCorrelation = value;
- peakIndex = i;
- }
- }
- if (peakIndex < 0) {
- ALOGE("%s() no signal for correlation\n", __func__);
- return -2;
- }
-#if 0
- // Dump correlation data for charting.
- else {
- const int32_t margin = 50;
- int32_t startIndex = std::max(0, peakIndex - margin);
- int32_t endIndex = std::min(numCorrelations - 1, peakIndex + margin);
- for (int32_t index = startIndex; index < endIndex; index++) {
- ALOGD("Correlation, %d, %f", index, correlations[index]);
- }
- }
-#endif
-
- report->latencyInFrames = recordedOffset + (peakIndex * stride);
- report->correlation = peakCorrelation;
-
- return 0;
-}
-
-#if USE_FAST_LATENCY_CALCULATION
-static int measureLatencyFromPulse(AudioRecording &recorded,
- AudioRecording &pulse,
- LatencyReport *report) {
- const int32_t coarseStride = 16;
- const int32_t fineWindowSize = coarseStride * 8;
- const int32_t fineStride = 1;
- LatencyReport courseReport;
- courseReport.reset();
- // Do a rough search, skipping over most of the samples.
- int result = measureLatencyFromPulsePartial(recorded,
- 0, // recordedOffset,
- recorded.size() - pulse.size(),
- pulse,
- &courseReport,
- coarseStride);
- if (result != 0) {
- return result;
- }
- // Now do a fine resolution search near the coarse latency result.
- int32_t recordedOffset = std::max(0, courseReport.latencyInFrames - (fineWindowSize / 2));
- result = measureLatencyFromPulsePartial(recorded,
- recordedOffset,
- fineWindowSize,
- pulse,
- report,
- fineStride );
- return result;
-}
-#else
-// TODO - When we are confident of the new code we can remove this old code.
-static int measureLatencyFromPulse(AudioRecording &recorded,
- AudioRecording &pulse,
- LatencyReport *report) {
- return measureLatencyFromPulsePartial(recorded,
- 0,
- recorded.size() - pulse.size(),
- pulse,
- report,
- 1 );
-}
-#endif
-
-// ====================================================================================
-class LoopbackProcessor {
-public:
- virtual ~LoopbackProcessor() = default;
-
- enum result_code {
- RESULT_OK = 0,
- ERROR_NOISY = -99,
- ERROR_VOLUME_TOO_LOW,
- ERROR_VOLUME_TOO_HIGH,
- ERROR_CONFIDENCE,
- ERROR_INVALID_STATE,
- ERROR_GLITCHES,
- ERROR_NO_LOCK
- };
-
- virtual void prepareToTest() {
- reset();
- }
-
- virtual void reset() {
- mResult = 0;
- mResetCount++;
- }
-
- virtual result_code processInputFrame(const float *frameData, int channelCount) = 0;
- virtual result_code processOutputFrame(float *frameData, int channelCount) = 0;
-
- void process(const float *inputData, int inputChannelCount, int numInputFrames,
- float *outputData, int outputChannelCount, int numOutputFrames) {
- int numBoth = std::min(numInputFrames, numOutputFrames);
- // Process one frame at a time.
- for (int i = 0; i < numBoth; i++) {
- processInputFrame(inputData, inputChannelCount);
- inputData += inputChannelCount;
- processOutputFrame(outputData, outputChannelCount);
- outputData += outputChannelCount;
- }
- // If there is more input than output.
- for (int i = numBoth; i < numInputFrames; i++) {
- processInputFrame(inputData, inputChannelCount);
- inputData += inputChannelCount;
- }
- // If there is more output than input.
- for (int i = numBoth; i < numOutputFrames; i++) {
- processOutputFrame(outputData, outputChannelCount);
- outputData += outputChannelCount;
- }
- }
-
- virtual std::string analyze() = 0;
-
- virtual void printStatus() {};
-
- int32_t getResult() {
- return mResult;
- }
-
- void setResult(int32_t result) {
- mResult = result;
- }
-
- virtual bool isDone() {
- return false;
- }
-
- virtual int save(const char *fileName) {
- (void) fileName;
- return -1;
- }
-
- virtual int load(const char *fileName) {
- (void) fileName;
- return -1;
- }
-
- virtual void setSampleRate(int32_t sampleRate) {
- mSampleRate = sampleRate;
- }
-
- int32_t getSampleRate() const {
- return mSampleRate;
- }
-
- int32_t getResetCount() const {
- return mResetCount;
- }
-
- /** Called when not enough input frames could be read after synchronization.
- */
- virtual void onInsufficientRead() {
- reset();
- }
-
- /**
- * Some analyzers may only look at one channel.
- * You can optionally specify that channel here.
- *
- * @param inputChannel
- */
- void setInputChannel(int inputChannel) {
- mInputChannel = inputChannel;
- }
-
- int getInputChannel() const {
- return mInputChannel;
- }
-
- /**
- * Some analyzers may only generate one channel.
- * You can optionally specify that channel here.
- *
- * @param outputChannel
- */
- void setOutputChannel(int outputChannel) {
- mOutputChannel = outputChannel;
- }
-
- int getOutputChannel() const {
- return mOutputChannel;
- }
-
-protected:
- int32_t mResetCount = 0;
-
-private:
-
- int32_t mInputChannel = 0;
- int32_t mOutputChannel = 0;
- int32_t mSampleRate = kDefaultSampleRate;
- int32_t mResult = 0;
-};
-
-class LatencyAnalyzer : public LoopbackProcessor {
-public:
-
- LatencyAnalyzer() : LoopbackProcessor() {}
- virtual ~LatencyAnalyzer() = default;
-
- /**
- * Call this after the constructor because it calls other virtual methods.
- */
- virtual void setup() = 0;
-
- virtual int32_t getProgress() const = 0;
-
- virtual int getState() const = 0;
-
- // @return latency in frames
- virtual int32_t getMeasuredLatency() const = 0;
-
- /**
- * This is an overall confidence in the latency result based on correlation, SNR, etc.
- * @return probability value between 0.0 and 1.0
- */
- double getMeasuredConfidence() const {
- // Limit the ratio and prevent divide-by-zero.
- double noiseSignalRatio = getSignalRMS() <= getBackgroundRMS()
- ? 1.0 : getBackgroundRMS() / getSignalRMS();
- // Prevent high background noise and low signals from generating false matches.
- double adjustedConfidence = getMeasuredCorrelation() - noiseSignalRatio;
- return std::max(0.0, adjustedConfidence);
- }
-
- /**
- * Cross correlation value for the noise pulse against
- * the corresponding position in the normalized recording.
- *
- * @return value between -1.0 and 1.0
- */
- virtual double getMeasuredCorrelation() const = 0;
-
- virtual double getBackgroundRMS() const = 0;
-
- virtual double getSignalRMS() const = 0;
-
- virtual bool hasEnoughData() const = 0;
-};
-
-// ====================================================================================
-/**
- * Measure latency given a loopback stream data.
- * Use an encoded bit train as the sound source because it
- * has an unambiguous correlation value.
- * Uses a state machine to cycle through various stages.
- *
- */
-class PulseLatencyAnalyzer : public LatencyAnalyzer {
-public:
-
- void setup() override {
- int32_t pulseLength = calculatePulseLength();
- int32_t maxLatencyFrames = getSampleRate() * kMaxLatencyMillis / kMillisPerSecond;
- mFramesToRecord = pulseLength + maxLatencyFrames;
- mAudioRecording.allocate(mFramesToRecord);
- mAudioRecording.setSampleRate(getSampleRate());
- }
-
- int getState() const override {
- return mState;
- }
-
- void setSampleRate(int32_t sampleRate) override {
- LoopbackProcessor::setSampleRate(sampleRate);
- mAudioRecording.setSampleRate(sampleRate);
- }
-
- void reset() override {
- LoopbackProcessor::reset();
- mState = STATE_MEASURE_BACKGROUND;
- mDownCounter = (int32_t) (getSampleRate() * kBackgroundMeasurementLengthSeconds);
- mLoopCounter = 0;
-
- mPulseCursor = 0;
- mBackgroundSumSquare = 0.0f;
- mBackgroundSumCount = 0;
- mBackgroundRMS = 0.0f;
- mSignalRMS = 0.0f;
-
- generatePulseRecording(calculatePulseLength());
- mAudioRecording.clear();
- mLatencyReport.reset();
- }
-
- bool hasEnoughData() const override {
- return mAudioRecording.isFull();
- }
-
- bool isDone() override {
- return mState == STATE_DONE;
- }
-
- int32_t getProgress() const override {
- return mAudioRecording.size();
- }
-
- std::string analyze() override {
- std::stringstream report;
- report << "PulseLatencyAnalyzer ---------------\n";
- report << LOOPBACK_RESULT_TAG "test.state = "
- << std::setw(8) << mState << "\n";
- report << LOOPBACK_RESULT_TAG "test.state.name = "
- << convertStateToText(mState) << "\n";
- report << LOOPBACK_RESULT_TAG "background.rms = "
- << std::setw(8) << mBackgroundRMS << "\n";
-
- int32_t newResult = RESULT_OK;
- if (mState != STATE_GOT_DATA) {
- report << "WARNING - Bad state. Check volume on device.\n";
- // setResult(ERROR_INVALID_STATE);
- } else {
- float gain = mAudioRecording.normalize(1.0f);
- measureLatency();
-
- // Calculate signalRMS even if it is bogus.
- // Also it may be used in the confidence calculation below.
- mSignalRMS = calculateRootMeanSquare(
- &mAudioRecording.getData()[mLatencyReport.latencyInFrames], mPulse.size())
- / gain;
- if (getMeasuredConfidence() < getMinimumConfidence()) {
- report << " ERROR - confidence too low!";
- newResult = ERROR_CONFIDENCE;
- }
-
- double latencyMillis = kMillisPerSecond * (double) mLatencyReport.latencyInFrames
- / getSampleRate();
- report << LOOPBACK_RESULT_TAG "latency.frames = " << std::setw(8)
- << mLatencyReport.latencyInFrames << "\n";
- report << LOOPBACK_RESULT_TAG "latency.msec = " << std::setw(8)
- << latencyMillis << "\n";
- report << LOOPBACK_RESULT_TAG "latency.confidence = " << std::setw(8)
- << getMeasuredConfidence() << "\n";
- report << LOOPBACK_RESULT_TAG "latency.correlation = " << std::setw(8)
- << getMeasuredCorrelation() << "\n";
- }
- mState = STATE_DONE;
- if (getResult() == RESULT_OK) {
- setResult(newResult);
- }
-
- return report.str();
- }
-
- int32_t getMeasuredLatency() const override {
- return mLatencyReport.latencyInFrames;
- }
-
- double getMeasuredCorrelation() const override {
- return mLatencyReport.correlation;
- }
-
- double getBackgroundRMS() const override {
- return mBackgroundRMS;
- }
-
- double getSignalRMS() const override {
- return mSignalRMS;
- }
-
- bool isRecordingComplete() {
- return mState == STATE_GOT_DATA;
- }
-
- void printStatus() override {
- ALOGD("latency: st = %d = %s", mState, convertStateToText(mState));
- }
-
- result_code processInputFrame(const float *frameData, int /* channelCount */) override {
- echo_state nextState = mState;
- mLoopCounter++;
- float input = frameData[0];
-
- switch (mState) {
- case STATE_MEASURE_BACKGROUND:
- // Measure background RMS on channel 0
- mBackgroundSumSquare += static_cast<double>(input) * input;
- mBackgroundSumCount++;
- mDownCounter--;
- if (mDownCounter <= 0) {
- mBackgroundRMS = sqrtf(mBackgroundSumSquare / mBackgroundSumCount);
- nextState = STATE_IN_PULSE;
- mPulseCursor = 0;
- }
- break;
-
- case STATE_IN_PULSE:
- // Record input until the mAudioRecording is full.
- mAudioRecording.write(input);
- if (hasEnoughData()) {
- nextState = STATE_GOT_DATA;
- }
- break;
-
- case STATE_GOT_DATA:
- case STATE_DONE:
- default:
- break;
- }
-
- mState = nextState;
- return RESULT_OK;
- }
-
- result_code processOutputFrame(float *frameData, int channelCount) override {
- switch (mState) {
- case STATE_IN_PULSE:
- if (mPulseCursor < mPulse.size()) {
- float pulseSample = mPulse.getData()[mPulseCursor++];
- for (int i = 0; i < channelCount; i++) {
- frameData[i] = pulseSample;
- }
- } else {
- for (int i = 0; i < channelCount; i++) {
- frameData[i] = 0;
- }
- }
- break;
-
- case STATE_MEASURE_BACKGROUND:
- case STATE_GOT_DATA:
- case STATE_DONE:
- default:
- for (int i = 0; i < channelCount; i++) {
- frameData[i] = 0.0f; // silence
- }
- break;
- }
-
- return RESULT_OK;
- }
-
-protected:
-
- virtual int32_t calculatePulseLength() const = 0;
-
- virtual void generatePulseRecording(int32_t pulseLength) = 0;
-
- virtual void measureLatency() = 0;
-
- virtual double getMinimumConfidence() const {
- return 0.5;
- }
-
- AudioRecording mPulse;
- AudioRecording mAudioRecording; // contains only the input after starting the pulse
- LatencyReport mLatencyReport;
-
- static constexpr int32_t kPulseLengthMillis = 500;
- float mPulseAmplitude = 0.5f;
- double mBackgroundRMS = 0.0;
- double mSignalRMS = 0.0;
-
-private:
-
- enum echo_state {
- STATE_MEASURE_BACKGROUND,
- STATE_IN_PULSE,
- STATE_GOT_DATA, // must match RoundTripLatencyActivity.java
- STATE_DONE,
- };
-
- const char *convertStateToText(echo_state state) {
- switch (state) {
- case STATE_MEASURE_BACKGROUND:
- return "INIT";
- case STATE_IN_PULSE:
- return "PULSE";
- case STATE_GOT_DATA:
- return "GOT_DATA";
- case STATE_DONE:
- return "DONE";
- }
- return "UNKNOWN";
- }
-
- int32_t mDownCounter = 500;
- int32_t mLoopCounter = 0;
- echo_state mState = STATE_MEASURE_BACKGROUND;
-
- static constexpr double kBackgroundMeasurementLengthSeconds = 0.5;
-
- int32_t mPulseCursor = 0;
-
- double mBackgroundSumSquare = 0.0;
- int32_t mBackgroundSumCount = 0;
- int32_t mFramesToRecord = 0;
-
-};
-
-/**
- * This algorithm uses a series of random bits encoded using the
- * Manchester encoder. It works well for wired loopback but not very well for
- * through the air loopback.
- */
-class EncodedRandomLatencyAnalyzer : public PulseLatencyAnalyzer {
-
-protected:
-
- int32_t calculatePulseLength() const override {
- // Calculate integer number of bits.
- int32_t numPulseBits = getSampleRate() * kPulseLengthMillis
- / (kFramesPerEncodedBit * kMillisPerSecond);
- return numPulseBits * kFramesPerEncodedBit;
- }
-
- void generatePulseRecording(int32_t pulseLength) override {
- mPulse.allocate(pulseLength);
- RandomPulseGenerator pulser(kFramesPerEncodedBit);
- for (int i = 0; i < pulseLength; i++) {
- mPulse.write(pulser.nextFloat() * mPulseAmplitude);
- }
- }
-
- double getMinimumConfidence() const override {
- return 0.2;
- }
-
- void measureLatency() override {
- measureLatencyFromPulse(mAudioRecording,
- mPulse,
- &mLatencyReport);
- }
-
-private:
- static constexpr int32_t kFramesPerEncodedBit = 8; // multiple of 2
-};
-
-/**
- * This algorithm uses White Noise sent in a short burst pattern.
- * The original signal and the recorded signal are then run through
- * an envelope follower to convert the fine detail into more of
- * a rectangular block before the correlation phase.
- */
-class WhiteNoiseLatencyAnalyzer : public PulseLatencyAnalyzer {
-
-protected:
-
- int32_t calculatePulseLength() const override {
- return getSampleRate() * kPulseLengthMillis / kMillisPerSecond;
- }
-
- void generatePulseRecording(int32_t pulseLength) override {
- mPulse.allocate(pulseLength);
- // Turn the noise on and off to sharpen the correlation peak.
- // Use more zeros than ones so that the correlation will be less than 0.5 even when there
- // is a strong background noise.
- int8_t pattern[] = {1, 0, 0,
- 1, 1, 0, 0, 0,
- 1, 1, 1, 0, 0, 0, 0,
- 1, 1, 1, 1, 0, 0, 0, 0, 0
- };
- PseudoRandom random;
- const int32_t numSections = sizeof(pattern);
- const int32_t framesPerSection = pulseLength / numSections;
- for (int section = 0; section < numSections; section++) {
- if (pattern[section]) {
- for (int i = 0; i < framesPerSection; i++) {
- mPulse.write((float) (random.nextRandomDouble() * mPulseAmplitude));
- }
- } else {
- for (int i = 0; i < framesPerSection; i++) {
- mPulse.write(0.0f);
- }
- }
- }
- // Write any remaining frames.
- int32_t framesWritten = framesPerSection * numSections;
- for (int i = framesWritten; i < pulseLength; i++) {
- mPulse.write(0.0f);
- }
- }
-
- void measureLatency() override {
- // Smooth out the noise so we see rectangular blocks.
- // This improves immunity against phase cancellation and distortion.
- static constexpr float decay = 0.99f; // just under 1.0, lower numbers decay faster
- mAudioRecording.detectPeaks(decay);
- mPulse.detectPeaks(decay);
- measureLatencyFromPulse(mAudioRecording,
- mPulse,
- &mLatencyReport);
- }
-
-};
-
-#endif // ANALYZER_LATENCY_ANALYZER_H