xref: /aosp_15_r20/cts/apps/CtsVerifier/jni/audio_loopback/NativeAudioAnalyzer.cpp (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1*b7c941bbSAndroid Build Coastguard Worker /*
2*b7c941bbSAndroid Build Coastguard Worker  * Copyright 2020 The Android Open Source Project
3*b7c941bbSAndroid Build Coastguard Worker  *
4*b7c941bbSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*b7c941bbSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*b7c941bbSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*b7c941bbSAndroid Build Coastguard Worker  *
8*b7c941bbSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*b7c941bbSAndroid Build Coastguard Worker  *
10*b7c941bbSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*b7c941bbSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*b7c941bbSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*b7c941bbSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*b7c941bbSAndroid Build Coastguard Worker  * limitations under the License.
15*b7c941bbSAndroid Build Coastguard Worker  */
16*b7c941bbSAndroid Build Coastguard Worker 
17*b7c941bbSAndroid Build Coastguard Worker #include "NativeAudioAnalyzer.h"
18*b7c941bbSAndroid Build Coastguard Worker 
19*b7c941bbSAndroid Build Coastguard Worker // AAudioStream_isMMapUsed() isn't a public symbol, so use dlsym() to access it.
20*b7c941bbSAndroid Build Coastguard Worker #include <dlfcn.h>
21*b7c941bbSAndroid Build Coastguard Worker #define LIB_AAUDIO_NAME "libaaudio.so"
22*b7c941bbSAndroid Build Coastguard Worker #define FUNCTION_IS_MMAP "AAudioStream_isMMapUsed"
23*b7c941bbSAndroid Build Coastguard Worker static bool (*aaudioStream_isMMap)(AAudioStream *stream) = nullptr;
24*b7c941bbSAndroid Build Coastguard Worker 
25*b7c941bbSAndroid Build Coastguard Worker #include "WavFileCapture.h"
26*b7c941bbSAndroid Build Coastguard Worker 
27*b7c941bbSAndroid Build Coastguard Worker extern WavFileCapture sWavFileCapture;
28*b7c941bbSAndroid Build Coastguard Worker 
convertPcm16ToFloat(const int16_t * source,float * destination,int32_t numSamples)29*b7c941bbSAndroid Build Coastguard Worker static void convertPcm16ToFloat(const int16_t *source,
30*b7c941bbSAndroid Build Coastguard Worker                                 float *destination,
31*b7c941bbSAndroid Build Coastguard Worker                                 int32_t numSamples) {
32*b7c941bbSAndroid Build Coastguard Worker     constexpr float scaler = 1.0f / 32768.0f;
33*b7c941bbSAndroid Build Coastguard Worker     for (int i = 0; i < numSamples; i++) {
34*b7c941bbSAndroid Build Coastguard Worker         destination[i] = source[i] * scaler;
35*b7c941bbSAndroid Build Coastguard Worker     }
36*b7c941bbSAndroid Build Coastguard Worker }
37*b7c941bbSAndroid Build Coastguard Worker 
38*b7c941bbSAndroid Build Coastguard Worker // Fill the audio output buffer.
readFormattedData(int32_t numFrames)39*b7c941bbSAndroid Build Coastguard Worker int32_t NativeAudioAnalyzer::readFormattedData(int32_t numFrames) {
40*b7c941bbSAndroid Build Coastguard Worker     int32_t framesRead = AAUDIO_ERROR_INVALID_FORMAT;
41*b7c941bbSAndroid Build Coastguard Worker     if (mActualInputFormat == AAUDIO_FORMAT_PCM_I16) {
42*b7c941bbSAndroid Build Coastguard Worker         framesRead = AAudioStream_read(mInputStream, mInputShortData,
43*b7c941bbSAndroid Build Coastguard Worker                                        numFrames,
44*b7c941bbSAndroid Build Coastguard Worker                                        0 /* timeoutNanoseconds */);
45*b7c941bbSAndroid Build Coastguard Worker     } else if (mActualInputFormat == AAUDIO_FORMAT_PCM_FLOAT) {
46*b7c941bbSAndroid Build Coastguard Worker         framesRead = AAudioStream_read(mInputStream, mInputFloatData,
47*b7c941bbSAndroid Build Coastguard Worker                                        numFrames,
48*b7c941bbSAndroid Build Coastguard Worker                                        0 /* timeoutNanoseconds */);
49*b7c941bbSAndroid Build Coastguard Worker     } else {
50*b7c941bbSAndroid Build Coastguard Worker         ALOGE("ERROR actualInputFormat = %d\n", mActualInputFormat);
51*b7c941bbSAndroid Build Coastguard Worker         assert(false);
52*b7c941bbSAndroid Build Coastguard Worker     }
53*b7c941bbSAndroid Build Coastguard Worker     if (framesRead < 0) {
54*b7c941bbSAndroid Build Coastguard Worker         // Expect INVALID_STATE if STATE_STARTING
55*b7c941bbSAndroid Build Coastguard Worker         if (mFramesReadTotal > 0) {
56*b7c941bbSAndroid Build Coastguard Worker             mInputError = framesRead;
57*b7c941bbSAndroid Build Coastguard Worker             ALOGE("ERROR in read = %d = %s\n", framesRead,
58*b7c941bbSAndroid Build Coastguard Worker                    AAudio_convertResultToText(framesRead));
59*b7c941bbSAndroid Build Coastguard Worker         } else {
60*b7c941bbSAndroid Build Coastguard Worker             framesRead = 0;
61*b7c941bbSAndroid Build Coastguard Worker         }
62*b7c941bbSAndroid Build Coastguard Worker     } else {
63*b7c941bbSAndroid Build Coastguard Worker         mFramesReadTotal += framesRead;
64*b7c941bbSAndroid Build Coastguard Worker     }
65*b7c941bbSAndroid Build Coastguard Worker     return framesRead;
66*b7c941bbSAndroid Build Coastguard Worker }
67*b7c941bbSAndroid Build Coastguard Worker 
has24BitSupport(aaudio_format_t format)68*b7c941bbSAndroid Build Coastguard Worker bool NativeAudioAnalyzer::has24BitSupport(aaudio_format_t format) {
69*b7c941bbSAndroid Build Coastguard Worker     return (format == AAUDIO_FORMAT_PCM_FLOAT) || (format == AAUDIO_FORMAT_PCM_I24_PACKED)
70*b7c941bbSAndroid Build Coastguard Worker             || (format == AAUDIO_FORMAT_PCM_I32);
71*b7c941bbSAndroid Build Coastguard Worker }
72*b7c941bbSAndroid Build Coastguard Worker 
dataCallbackProc(void * audioData,int32_t numFrames)73*b7c941bbSAndroid Build Coastguard Worker aaudio_data_callback_result_t NativeAudioAnalyzer::dataCallbackProc(
74*b7c941bbSAndroid Build Coastguard Worker         void *audioData,
75*b7c941bbSAndroid Build Coastguard Worker         int32_t numFrames
76*b7c941bbSAndroid Build Coastguard Worker ) {
77*b7c941bbSAndroid Build Coastguard Worker     aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE;
78*b7c941bbSAndroid Build Coastguard Worker     float  *outputData = (float  *) audioData;
79*b7c941bbSAndroid Build Coastguard Worker 
80*b7c941bbSAndroid Build Coastguard Worker     // Read audio data from the input stream.
81*b7c941bbSAndroid Build Coastguard Worker     int32_t actualFramesRead;
82*b7c941bbSAndroid Build Coastguard Worker 
83*b7c941bbSAndroid Build Coastguard Worker     if (numFrames > mInputFramesMaximum) {
84*b7c941bbSAndroid Build Coastguard Worker         ALOGE("%s() numFrames:%d > mInputFramesMaximum:%d", __func__, numFrames, mInputFramesMaximum);
85*b7c941bbSAndroid Build Coastguard Worker         mInputError = AAUDIO_ERROR_OUT_OF_RANGE;
86*b7c941bbSAndroid Build Coastguard Worker         return AAUDIO_CALLBACK_RESULT_STOP;
87*b7c941bbSAndroid Build Coastguard Worker     }
88*b7c941bbSAndroid Build Coastguard Worker 
89*b7c941bbSAndroid Build Coastguard Worker     if (numFrames > mMaxNumFrames) {
90*b7c941bbSAndroid Build Coastguard Worker         mMaxNumFrames = numFrames;
91*b7c941bbSAndroid Build Coastguard Worker     }
92*b7c941bbSAndroid Build Coastguard Worker     if (numFrames < mMinNumFrames) {
93*b7c941bbSAndroid Build Coastguard Worker         mMinNumFrames = numFrames;
94*b7c941bbSAndroid Build Coastguard Worker     }
95*b7c941bbSAndroid Build Coastguard Worker 
96*b7c941bbSAndroid Build Coastguard Worker     // Get atomic snapshot of the relative frame positions so they
97*b7c941bbSAndroid Build Coastguard Worker     // can be used to calculate timestamp latency.
98*b7c941bbSAndroid Build Coastguard Worker     int64_t framesRead = AAudioStream_getFramesRead(mInputStream);
99*b7c941bbSAndroid Build Coastguard Worker     int64_t framesWritten = AAudioStream_getFramesWritten(mOutputStream);
100*b7c941bbSAndroid Build Coastguard Worker     mWriteReadDelta = framesWritten - framesRead;
101*b7c941bbSAndroid Build Coastguard Worker     mWriteReadDeltaValid = true;
102*b7c941bbSAndroid Build Coastguard Worker 
103*b7c941bbSAndroid Build Coastguard Worker     // Silence the output.
104*b7c941bbSAndroid Build Coastguard Worker     int32_t numBytes = numFrames * mActualOutputChannelCount * sizeof(float);
105*b7c941bbSAndroid Build Coastguard Worker     memset(audioData, 0 /* value */, numBytes);
106*b7c941bbSAndroid Build Coastguard Worker 
107*b7c941bbSAndroid Build Coastguard Worker     if (mNumCallbacksToDrain > 0) {
108*b7c941bbSAndroid Build Coastguard Worker         // Drain the input FIFOs.
109*b7c941bbSAndroid Build Coastguard Worker         int32_t totalFramesRead = 0;
110*b7c941bbSAndroid Build Coastguard Worker         do {
111*b7c941bbSAndroid Build Coastguard Worker             actualFramesRead = readFormattedData(numFrames);
112*b7c941bbSAndroid Build Coastguard Worker             if (actualFramesRead > 0) {
113*b7c941bbSAndroid Build Coastguard Worker                 totalFramesRead += actualFramesRead;
114*b7c941bbSAndroid Build Coastguard Worker             } else if (actualFramesRead < 0) {
115*b7c941bbSAndroid Build Coastguard Worker                 callbackResult = AAUDIO_CALLBACK_RESULT_STOP;
116*b7c941bbSAndroid Build Coastguard Worker             }
117*b7c941bbSAndroid Build Coastguard Worker             // Ignore errors because input stream may not be started yet.
118*b7c941bbSAndroid Build Coastguard Worker         } while (actualFramesRead > 0);
119*b7c941bbSAndroid Build Coastguard Worker         // Only counts if we actually got some data.
120*b7c941bbSAndroid Build Coastguard Worker         if (totalFramesRead > 0) {
121*b7c941bbSAndroid Build Coastguard Worker             mNumCallbacksToDrain--;
122*b7c941bbSAndroid Build Coastguard Worker         }
123*b7c941bbSAndroid Build Coastguard Worker 
124*b7c941bbSAndroid Build Coastguard Worker     } else if (mNumCallbacksToNotRead > 0) {
125*b7c941bbSAndroid Build Coastguard Worker         // Let the input fill up a bit so we are not so close to the write pointer.
126*b7c941bbSAndroid Build Coastguard Worker         mNumCallbacksToNotRead--;
127*b7c941bbSAndroid Build Coastguard Worker     } else if (mNumCallbacksToDiscard > 0) {
128*b7c941bbSAndroid Build Coastguard Worker         // Ignore. Allow the input to fill back up to equilibrium with the output.
129*b7c941bbSAndroid Build Coastguard Worker         actualFramesRead = readFormattedData(numFrames);
130*b7c941bbSAndroid Build Coastguard Worker         if (actualFramesRead < 0) {
131*b7c941bbSAndroid Build Coastguard Worker             callbackResult = AAUDIO_CALLBACK_RESULT_STOP;
132*b7c941bbSAndroid Build Coastguard Worker         }
133*b7c941bbSAndroid Build Coastguard Worker         mNumCallbacksToDiscard--;
134*b7c941bbSAndroid Build Coastguard Worker 
135*b7c941bbSAndroid Build Coastguard Worker     } else {
136*b7c941bbSAndroid Build Coastguard Worker         // The full duplex stream is now stable so process the audio.
137*b7c941bbSAndroid Build Coastguard Worker         int32_t numInputBytes = numFrames * mActualInputChannelCount * sizeof(float);
138*b7c941bbSAndroid Build Coastguard Worker         memset(mInputFloatData, 0 /* value */, numInputBytes);
139*b7c941bbSAndroid Build Coastguard Worker 
140*b7c941bbSAndroid Build Coastguard Worker         int64_t inputFramesWritten = AAudioStream_getFramesWritten(mInputStream);
141*b7c941bbSAndroid Build Coastguard Worker         int64_t inputFramesRead = AAudioStream_getFramesRead(mInputStream);
142*b7c941bbSAndroid Build Coastguard Worker         int64_t framesAvailable = inputFramesWritten - inputFramesRead;
143*b7c941bbSAndroid Build Coastguard Worker 
144*b7c941bbSAndroid Build Coastguard Worker         // Read the INPUT data.
145*b7c941bbSAndroid Build Coastguard Worker         actualFramesRead = readFormattedData(numFrames); // READ
146*b7c941bbSAndroid Build Coastguard Worker         if (actualFramesRead < 0) {
147*b7c941bbSAndroid Build Coastguard Worker             callbackResult = AAUDIO_CALLBACK_RESULT_STOP;
148*b7c941bbSAndroid Build Coastguard Worker         } else {
149*b7c941bbSAndroid Build Coastguard Worker             if (actualFramesRead < numFrames) {
150*b7c941bbSAndroid Build Coastguard Worker                 if(actualFramesRead < (int32_t) framesAvailable) {
151*b7c941bbSAndroid Build Coastguard Worker                     ALOGE("insufficient for no reason, numFrames = %d"
152*b7c941bbSAndroid Build Coastguard Worker                                    ", actualFramesRead = %d"
153*b7c941bbSAndroid Build Coastguard Worker                                    ", inputFramesWritten = %d"
154*b7c941bbSAndroid Build Coastguard Worker                                    ", inputFramesRead = %d"
155*b7c941bbSAndroid Build Coastguard Worker                                    ", available = %d\n",
156*b7c941bbSAndroid Build Coastguard Worker                            numFrames,
157*b7c941bbSAndroid Build Coastguard Worker                            actualFramesRead,
158*b7c941bbSAndroid Build Coastguard Worker                            (int) inputFramesWritten,
159*b7c941bbSAndroid Build Coastguard Worker                            (int) inputFramesRead,
160*b7c941bbSAndroid Build Coastguard Worker                            (int) framesAvailable);
161*b7c941bbSAndroid Build Coastguard Worker                 }
162*b7c941bbSAndroid Build Coastguard Worker                 mInsufficientReadCount++;
163*b7c941bbSAndroid Build Coastguard Worker                 mInsufficientReadFrames += numFrames - actualFramesRead; // deficit
164*b7c941bbSAndroid Build Coastguard Worker                 // ALOGE("Error insufficientReadCount = %d\n",(int)mInsufficientReadCount);
165*b7c941bbSAndroid Build Coastguard Worker             }
166*b7c941bbSAndroid Build Coastguard Worker 
167*b7c941bbSAndroid Build Coastguard Worker             int32_t numSamples = actualFramesRead * mActualInputChannelCount;
168*b7c941bbSAndroid Build Coastguard Worker 
169*b7c941bbSAndroid Build Coastguard Worker             if (mActualInputFormat == AAUDIO_FORMAT_PCM_I16) {
170*b7c941bbSAndroid Build Coastguard Worker                 convertPcm16ToFloat(mInputShortData, mInputFloatData, numSamples);
171*b7c941bbSAndroid Build Coastguard Worker             }
172*b7c941bbSAndroid Build Coastguard Worker 
173*b7c941bbSAndroid Build Coastguard Worker             // Process the INPUT and generate the OUTPUT.
174*b7c941bbSAndroid Build Coastguard Worker             mLoopbackProcessor->process(mInputFloatData,
175*b7c941bbSAndroid Build Coastguard Worker                                                mActualInputChannelCount,
176*b7c941bbSAndroid Build Coastguard Worker                                                numFrames,
177*b7c941bbSAndroid Build Coastguard Worker                                                outputData,
178*b7c941bbSAndroid Build Coastguard Worker                                                mActualOutputChannelCount,
179*b7c941bbSAndroid Build Coastguard Worker                                                numFrames);
180*b7c941bbSAndroid Build Coastguard Worker 
181*b7c941bbSAndroid Build Coastguard Worker             sWavFileCapture.captureData(audioData, numFrames);
182*b7c941bbSAndroid Build Coastguard Worker 
183*b7c941bbSAndroid Build Coastguard Worker             mIsDone = mLoopbackProcessor->isDone();
184*b7c941bbSAndroid Build Coastguard Worker             if (mIsDone) {
185*b7c941bbSAndroid Build Coastguard Worker                 callbackResult = AAUDIO_CALLBACK_RESULT_STOP;
186*b7c941bbSAndroid Build Coastguard Worker             }
187*b7c941bbSAndroid Build Coastguard Worker         }
188*b7c941bbSAndroid Build Coastguard Worker     }
189*b7c941bbSAndroid Build Coastguard Worker     mFramesWrittenTotal += numFrames;
190*b7c941bbSAndroid Build Coastguard Worker 
191*b7c941bbSAndroid Build Coastguard Worker     return callbackResult;
192*b7c941bbSAndroid Build Coastguard Worker }
193*b7c941bbSAndroid Build Coastguard Worker 
s_MyDataCallbackProc(AAudioStream *,void * userData,void * audioData,int32_t numFrames)194*b7c941bbSAndroid Build Coastguard Worker static aaudio_data_callback_result_t s_MyDataCallbackProc(
195*b7c941bbSAndroid Build Coastguard Worker         AAudioStream * /* outputStream */,
196*b7c941bbSAndroid Build Coastguard Worker         void *userData,
197*b7c941bbSAndroid Build Coastguard Worker         void *audioData,
198*b7c941bbSAndroid Build Coastguard Worker         int32_t numFrames) {
199*b7c941bbSAndroid Build Coastguard Worker     NativeAudioAnalyzer *myData = (NativeAudioAnalyzer *) userData;
200*b7c941bbSAndroid Build Coastguard Worker     return myData->dataCallbackProc(audioData, numFrames);
201*b7c941bbSAndroid Build Coastguard Worker }
202*b7c941bbSAndroid Build Coastguard Worker 
s_MyErrorCallbackProc(AAudioStream *,void * userData,aaudio_result_t error)203*b7c941bbSAndroid Build Coastguard Worker static void s_MyErrorCallbackProc(
204*b7c941bbSAndroid Build Coastguard Worker         AAudioStream * /* stream */,
205*b7c941bbSAndroid Build Coastguard Worker         void * userData,
206*b7c941bbSAndroid Build Coastguard Worker         aaudio_result_t error) {
207*b7c941bbSAndroid Build Coastguard Worker     ALOGE("Error Callback, error: %d\n",(int)error);
208*b7c941bbSAndroid Build Coastguard Worker     NativeAudioAnalyzer *myData = (NativeAudioAnalyzer *) userData;
209*b7c941bbSAndroid Build Coastguard Worker     myData->mOutputError = error;
210*b7c941bbSAndroid Build Coastguard Worker }
211*b7c941bbSAndroid Build Coastguard Worker 
isRecordingComplete()212*b7c941bbSAndroid Build Coastguard Worker bool NativeAudioAnalyzer::isRecordingComplete() {
213*b7c941bbSAndroid Build Coastguard Worker     return mWhiteNoiseLatencyAnalyzer.isRecordingComplete();
214*b7c941bbSAndroid Build Coastguard Worker }
215*b7c941bbSAndroid Build Coastguard Worker 
analyze()216*b7c941bbSAndroid Build Coastguard Worker int NativeAudioAnalyzer::analyze() {
217*b7c941bbSAndroid Build Coastguard Worker     mWhiteNoiseLatencyAnalyzer.analyze();
218*b7c941bbSAndroid Build Coastguard Worker     return getError(); // TODO review
219*b7c941bbSAndroid Build Coastguard Worker }
220*b7c941bbSAndroid Build Coastguard Worker 
getLatencyMillis()221*b7c941bbSAndroid Build Coastguard Worker double NativeAudioAnalyzer::getLatencyMillis() {
222*b7c941bbSAndroid Build Coastguard Worker     return mWhiteNoiseLatencyAnalyzer.getMeasuredLatency() * 1000.0 / 48000;
223*b7c941bbSAndroid Build Coastguard Worker }
224*b7c941bbSAndroid Build Coastguard Worker 
getConfidence()225*b7c941bbSAndroid Build Coastguard Worker double NativeAudioAnalyzer::getConfidence() {
226*b7c941bbSAndroid Build Coastguard Worker     return mWhiteNoiseLatencyAnalyzer.getMeasuredConfidence();
227*b7c941bbSAndroid Build Coastguard Worker }
228*b7c941bbSAndroid Build Coastguard Worker 
has24BitHardwareSupport()229*b7c941bbSAndroid Build Coastguard Worker bool NativeAudioAnalyzer::has24BitHardwareSupport() {
230*b7c941bbSAndroid Build Coastguard Worker     return mHas24BitHardwareSupport;
231*b7c941bbSAndroid Build Coastguard Worker }
232*b7c941bbSAndroid Build Coastguard Worker 
getHardwareFormat()233*b7c941bbSAndroid Build Coastguard Worker int NativeAudioAnalyzer::getHardwareFormat() {
234*b7c941bbSAndroid Build Coastguard Worker     return mHardwareFormat;
235*b7c941bbSAndroid Build Coastguard Worker }
236*b7c941bbSAndroid Build Coastguard Worker 
getSampleRate()237*b7c941bbSAndroid Build Coastguard Worker int NativeAudioAnalyzer::getSampleRate() {
238*b7c941bbSAndroid Build Coastguard Worker     return mOutputSampleRate;
239*b7c941bbSAndroid Build Coastguard Worker }
240*b7c941bbSAndroid Build Coastguard Worker 
openAudio(int inputDeviceId,int outputDeviceId)241*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t NativeAudioAnalyzer::openAudio(int inputDeviceId, int outputDeviceId) {
242*b7c941bbSAndroid Build Coastguard Worker     mInputDeviceId = inputDeviceId;
243*b7c941bbSAndroid Build Coastguard Worker     mOutputDeviceId = outputDeviceId;
244*b7c941bbSAndroid Build Coastguard Worker 
245*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder *builder = nullptr;
246*b7c941bbSAndroid Build Coastguard Worker 
247*b7c941bbSAndroid Build Coastguard Worker     mWhiteNoiseLatencyAnalyzer.setup();
248*b7c941bbSAndroid Build Coastguard Worker     mLoopbackProcessor = &mWhiteNoiseLatencyAnalyzer; // for latency test
249*b7c941bbSAndroid Build Coastguard Worker 
250*b7c941bbSAndroid Build Coastguard Worker     // Use an AAudioStreamBuilder to contain requested parameters.
251*b7c941bbSAndroid Build Coastguard Worker     aaudio_result_t result = AAudio_createStreamBuilder(&builder);
252*b7c941bbSAndroid Build Coastguard Worker     if (result != AAUDIO_OK) {
253*b7c941bbSAndroid Build Coastguard Worker         ALOGE("AAudio_createStreamBuilder() returned %s",
254*b7c941bbSAndroid Build Coastguard Worker                AAudio_convertResultToText(result));
255*b7c941bbSAndroid Build Coastguard Worker         return result;
256*b7c941bbSAndroid Build Coastguard Worker     }
257*b7c941bbSAndroid Build Coastguard Worker 
258*b7c941bbSAndroid Build Coastguard Worker     // Create the OUTPUT stream -----------------------
259*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
260*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
261*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
262*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
263*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setChannelCount(builder, 2); // stereo
264*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setDataCallback(builder, s_MyDataCallbackProc, this);
265*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setErrorCallback(builder, s_MyErrorCallbackProc, this);
266*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setDeviceId(builder, mOutputDeviceId);
267*b7c941bbSAndroid Build Coastguard Worker 
268*b7c941bbSAndroid Build Coastguard Worker     result = AAudioStreamBuilder_openStream(builder, &mOutputStream);
269*b7c941bbSAndroid Build Coastguard Worker     if (result != AAUDIO_OK) {
270*b7c941bbSAndroid Build Coastguard Worker         ALOGE("NativeAudioAnalyzer::openAudio() OUTPUT error %s",
271*b7c941bbSAndroid Build Coastguard Worker                AAudio_convertResultToText(result));
272*b7c941bbSAndroid Build Coastguard Worker         return result;
273*b7c941bbSAndroid Build Coastguard Worker     }
274*b7c941bbSAndroid Build Coastguard Worker 
275*b7c941bbSAndroid Build Coastguard Worker     mHardwareFormat = AAudioStream_getHardwareFormat(mOutputStream);
276*b7c941bbSAndroid Build Coastguard Worker     mHas24BitHardwareSupport = has24BitSupport(mHardwareFormat);
277*b7c941bbSAndroid Build Coastguard Worker 
278*b7c941bbSAndroid Build Coastguard Worker     // Stream Attributes
279*b7c941bbSAndroid Build Coastguard Worker     mBurstFrames[STREAM_OUTPUT] = AAudioStream_getFramesPerBurst(mOutputStream);
280*b7c941bbSAndroid Build Coastguard Worker     (void) AAudioStream_setBufferSizeInFrames(mOutputStream,
281*b7c941bbSAndroid Build Coastguard Worker             mBurstFrames[STREAM_OUTPUT] * kDefaultOutputSizeBursts);
282*b7c941bbSAndroid Build Coastguard Worker     mCapacityFrames[STREAM_OUTPUT] = AAudioStream_getBufferCapacityInFrames(mOutputStream);
283*b7c941bbSAndroid Build Coastguard Worker     mIsLowLatencyStream[STREAM_OUTPUT] =
284*b7c941bbSAndroid Build Coastguard Worker             AAudioStream_getPerformanceMode(mOutputStream) == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
285*b7c941bbSAndroid Build Coastguard Worker 
286*b7c941bbSAndroid Build Coastguard Worker     // Late binding for aaudioStream_isMMap()
287*b7c941bbSAndroid Build Coastguard Worker     if (aaudioStream_isMMap == nullptr) {
288*b7c941bbSAndroid Build Coastguard Worker         void* libHandle = dlopen(LIB_AAUDIO_NAME, RTLD_NOW);
289*b7c941bbSAndroid Build Coastguard Worker         aaudioStream_isMMap = (bool (*)(AAudioStream *stream))
290*b7c941bbSAndroid Build Coastguard Worker                 dlsym(libHandle, FUNCTION_IS_MMAP);
291*b7c941bbSAndroid Build Coastguard Worker         if (aaudioStream_isMMap == nullptr) {
292*b7c941bbSAndroid Build Coastguard Worker             ALOGE("%s() could not find " FUNCTION_IS_MMAP, __func__);
293*b7c941bbSAndroid Build Coastguard Worker         }
294*b7c941bbSAndroid Build Coastguard Worker     }
295*b7c941bbSAndroid Build Coastguard Worker     mIsMMap[STREAM_OUTPUT] =
296*b7c941bbSAndroid Build Coastguard Worker             aaudioStream_isMMap != nullptr ? aaudioStream_isMMap(mOutputStream) : false;
297*b7c941bbSAndroid Build Coastguard Worker 
298*b7c941bbSAndroid Build Coastguard Worker     mOutputSampleRate = AAudioStream_getSampleRate(mOutputStream);
299*b7c941bbSAndroid Build Coastguard Worker     mActualOutputChannelCount = AAudioStream_getChannelCount(mOutputStream);
300*b7c941bbSAndroid Build Coastguard Worker 
301*b7c941bbSAndroid Build Coastguard Worker     // Create the INPUT stream -----------------------
302*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
303*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_UNSPECIFIED);
304*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setSampleRate(builder, mOutputSampleRate); // must match
305*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setChannelCount(builder, 1); // mono
306*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setDataCallback(builder, nullptr, nullptr);
307*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setErrorCallback(builder, nullptr, nullptr);
308*b7c941bbSAndroid Build Coastguard Worker     AAudioStreamBuilder_setDeviceId(builder, mInputDeviceId);
309*b7c941bbSAndroid Build Coastguard Worker 
310*b7c941bbSAndroid Build Coastguard Worker     result = AAudioStreamBuilder_openStream(builder, &mInputStream);
311*b7c941bbSAndroid Build Coastguard Worker     if (result != AAUDIO_OK) {
312*b7c941bbSAndroid Build Coastguard Worker         ALOGE("NativeAudioAnalyzer::openAudio() INPUT error %s",
313*b7c941bbSAndroid Build Coastguard Worker                AAudio_convertResultToText(result));
314*b7c941bbSAndroid Build Coastguard Worker         return result;
315*b7c941bbSAndroid Build Coastguard Worker     }
316*b7c941bbSAndroid Build Coastguard Worker 
317*b7c941bbSAndroid Build Coastguard Worker     // Stream Attributes
318*b7c941bbSAndroid Build Coastguard Worker     mIsLowLatencyStream[STREAM_INPUT] =
319*b7c941bbSAndroid Build Coastguard Worker             AAudioStream_getPerformanceMode(mInputStream) == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
320*b7c941bbSAndroid Build Coastguard Worker     mBurstFrames[STREAM_INPUT] = AAudioStream_getFramesPerBurst(mInputStream);
321*b7c941bbSAndroid Build Coastguard Worker     mCapacityFrames[STREAM_INPUT] = AAudioStream_getBufferCapacityInFrames(mInputStream);
322*b7c941bbSAndroid Build Coastguard Worker     mIsMMap[STREAM_INPUT] =
323*b7c941bbSAndroid Build Coastguard Worker             aaudioStream_isMMap != nullptr ? aaudioStream_isMMap(mInputStream) : false;
324*b7c941bbSAndroid Build Coastguard Worker     int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(mInputStream);
325*b7c941bbSAndroid Build Coastguard Worker     (void) AAudioStream_setBufferSizeInFrames(mInputStream, actualCapacity);
326*b7c941bbSAndroid Build Coastguard Worker 
327*b7c941bbSAndroid Build Coastguard Worker     // ------- Setup loopbackData -----------------------------
328*b7c941bbSAndroid Build Coastguard Worker     mActualInputFormat = AAudioStream_getFormat(mInputStream);
329*b7c941bbSAndroid Build Coastguard Worker     mActualInputChannelCount = AAudioStream_getChannelCount(mInputStream);
330*b7c941bbSAndroid Build Coastguard Worker 
331*b7c941bbSAndroid Build Coastguard Worker     // Allocate a buffer for the audio data.
332*b7c941bbSAndroid Build Coastguard Worker     mInputFramesMaximum = 32 * AAudioStream_getFramesPerBurst(mInputStream);
333*b7c941bbSAndroid Build Coastguard Worker 
334*b7c941bbSAndroid Build Coastguard Worker     if (mActualInputFormat == AAUDIO_FORMAT_PCM_I16) {
335*b7c941bbSAndroid Build Coastguard Worker         mInputShortData = new int16_t[mInputFramesMaximum * mActualInputChannelCount]{};
336*b7c941bbSAndroid Build Coastguard Worker     }
337*b7c941bbSAndroid Build Coastguard Worker     mInputFloatData = new float[mInputFramesMaximum * mActualInputChannelCount]{};
338*b7c941bbSAndroid Build Coastguard Worker 
339*b7c941bbSAndroid Build Coastguard Worker     return result;
340*b7c941bbSAndroid Build Coastguard Worker }
341*b7c941bbSAndroid Build Coastguard Worker 
startAudio()342*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t NativeAudioAnalyzer::startAudio() {
343*b7c941bbSAndroid Build Coastguard Worker     mLoopbackProcessor->prepareToTest();
344*b7c941bbSAndroid Build Coastguard Worker 
345*b7c941bbSAndroid Build Coastguard Worker     mWriteReadDeltaValid = false;
346*b7c941bbSAndroid Build Coastguard Worker 
347*b7c941bbSAndroid Build Coastguard Worker     // Start OUTPUT first so INPUT does not overflow.
348*b7c941bbSAndroid Build Coastguard Worker     aaudio_result_t result = AAudioStream_requestStart(mOutputStream);
349*b7c941bbSAndroid Build Coastguard Worker     if (result != AAUDIO_OK) {
350*b7c941bbSAndroid Build Coastguard Worker         stopAudio();
351*b7c941bbSAndroid Build Coastguard Worker         return result;
352*b7c941bbSAndroid Build Coastguard Worker     }
353*b7c941bbSAndroid Build Coastguard Worker 
354*b7c941bbSAndroid Build Coastguard Worker     result = AAudioStream_requestStart(mInputStream);
355*b7c941bbSAndroid Build Coastguard Worker     if (result != AAUDIO_OK) {
356*b7c941bbSAndroid Build Coastguard Worker         stopAudio();
357*b7c941bbSAndroid Build Coastguard Worker         return result;
358*b7c941bbSAndroid Build Coastguard Worker     }
359*b7c941bbSAndroid Build Coastguard Worker 
360*b7c941bbSAndroid Build Coastguard Worker     return result;
361*b7c941bbSAndroid Build Coastguard Worker }
362*b7c941bbSAndroid Build Coastguard Worker 
stopAudio()363*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t NativeAudioAnalyzer::stopAudio() {
364*b7c941bbSAndroid Build Coastguard Worker     aaudio_result_t result1 = AAUDIO_OK;
365*b7c941bbSAndroid Build Coastguard Worker     aaudio_result_t result2 = AAUDIO_OK;
366*b7c941bbSAndroid Build Coastguard Worker     ALOGD("stopAudio() , minNumFrames = %d, maxNumFrames = %d\n", mMinNumFrames, mMaxNumFrames);
367*b7c941bbSAndroid Build Coastguard Worker     // Stop OUTPUT first because it uses INPUT.
368*b7c941bbSAndroid Build Coastguard Worker     if (mOutputStream != nullptr) {
369*b7c941bbSAndroid Build Coastguard Worker         result1 = AAudioStream_requestStop(mOutputStream);
370*b7c941bbSAndroid Build Coastguard Worker     }
371*b7c941bbSAndroid Build Coastguard Worker 
372*b7c941bbSAndroid Build Coastguard Worker     // Stop INPUT.
373*b7c941bbSAndroid Build Coastguard Worker     if (mInputStream != nullptr) {
374*b7c941bbSAndroid Build Coastguard Worker         result2 = AAudioStream_requestStop(mInputStream);
375*b7c941bbSAndroid Build Coastguard Worker     }
376*b7c941bbSAndroid Build Coastguard Worker     return result1 != AAUDIO_OK ? result1 : result2;
377*b7c941bbSAndroid Build Coastguard Worker }
378*b7c941bbSAndroid Build Coastguard Worker 
closeAudio()379*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t NativeAudioAnalyzer::closeAudio() {
380*b7c941bbSAndroid Build Coastguard Worker     aaudio_result_t result1 = AAUDIO_OK;
381*b7c941bbSAndroid Build Coastguard Worker     aaudio_result_t result2 = AAUDIO_OK;
382*b7c941bbSAndroid Build Coastguard Worker     // Stop and close OUTPUT first because it uses INPUT.
383*b7c941bbSAndroid Build Coastguard Worker     if (mOutputStream != nullptr) {
384*b7c941bbSAndroid Build Coastguard Worker         result1 = AAudioStream_close(mOutputStream);
385*b7c941bbSAndroid Build Coastguard Worker         mOutputStream = nullptr;
386*b7c941bbSAndroid Build Coastguard Worker     }
387*b7c941bbSAndroid Build Coastguard Worker 
388*b7c941bbSAndroid Build Coastguard Worker     // Stop and close INPUT.
389*b7c941bbSAndroid Build Coastguard Worker     if (mInputStream != nullptr) {
390*b7c941bbSAndroid Build Coastguard Worker         result2 = AAudioStream_close(mInputStream);
391*b7c941bbSAndroid Build Coastguard Worker         mInputStream = nullptr;
392*b7c941bbSAndroid Build Coastguard Worker     }
393*b7c941bbSAndroid Build Coastguard Worker     return result1 != AAUDIO_OK ? result1 : result2;
394*b7c941bbSAndroid Build Coastguard Worker }
395*b7c941bbSAndroid Build Coastguard Worker 
396*b7c941bbSAndroid Build Coastguard Worker // The timestamp latency is the difference between the input
397*b7c941bbSAndroid Build Coastguard Worker // and output times for a specific frame.
398*b7c941bbSAndroid Build Coastguard Worker // Start with the position and time from an input timestamp.
399*b7c941bbSAndroid Build Coastguard Worker // Map the input position to the corresponding position in output
400*b7c941bbSAndroid Build Coastguard Worker // and calculate its time.
401*b7c941bbSAndroid Build Coastguard Worker // Use the difference between framesWritten and framesRead to
402*b7c941bbSAndroid Build Coastguard Worker // convert input positions to output positions.
403*b7c941bbSAndroid Build Coastguard Worker // Returns -1.0 if the data callback wasn't called or if the stream is closed.
measureTimestampLatencyMillis()404*b7c941bbSAndroid Build Coastguard Worker double NativeAudioAnalyzer::measureTimestampLatencyMillis() {
405*b7c941bbSAndroid Build Coastguard Worker     if (!mWriteReadDeltaValid) return -1.0; // Data callback never called.
406*b7c941bbSAndroid Build Coastguard Worker 
407*b7c941bbSAndroid Build Coastguard Worker     int64_t writeReadDelta = mWriteReadDelta;
408*b7c941bbSAndroid Build Coastguard Worker     aaudio_result_t result;
409*b7c941bbSAndroid Build Coastguard Worker 
410*b7c941bbSAndroid Build Coastguard Worker     int64_t inputPosition;
411*b7c941bbSAndroid Build Coastguard Worker     int64_t inputTimeNanos;
412*b7c941bbSAndroid Build Coastguard Worker     int64_t outputPosition;
413*b7c941bbSAndroid Build Coastguard Worker     int64_t outputTimeNanos;
414*b7c941bbSAndroid Build Coastguard Worker     result = AAudioStream_getTimestamp(mInputStream, CLOCK_MONOTONIC, &inputPosition,
415*b7c941bbSAndroid Build Coastguard Worker                                        &inputTimeNanos);
416*b7c941bbSAndroid Build Coastguard Worker     if (result != AAUDIO_OK) {
417*b7c941bbSAndroid Build Coastguard Worker         return -1.0; // Stream is closed.
418*b7c941bbSAndroid Build Coastguard Worker     }
419*b7c941bbSAndroid Build Coastguard Worker     result = AAudioStream_getTimestamp(mOutputStream, CLOCK_MONOTONIC, &outputPosition,
420*b7c941bbSAndroid Build Coastguard Worker                                        &outputTimeNanos);
421*b7c941bbSAndroid Build Coastguard Worker     if (result != AAUDIO_OK) {
422*b7c941bbSAndroid Build Coastguard Worker         return -1.0; // Stream is closed.
423*b7c941bbSAndroid Build Coastguard Worker     }
424*b7c941bbSAndroid Build Coastguard Worker 
425*b7c941bbSAndroid Build Coastguard Worker     // Map input frame position to the corresponding output frame.
426*b7c941bbSAndroid Build Coastguard Worker     int64_t mappedPosition = inputPosition + writeReadDelta;
427*b7c941bbSAndroid Build Coastguard Worker     // Calculate when that frame will play.
428*b7c941bbSAndroid Build Coastguard Worker     int32_t sampleRate = getSampleRate();
429*b7c941bbSAndroid Build Coastguard Worker     int64_t mappedTimeNanos = outputTimeNanos + ((mappedPosition - outputPosition) * 1e9)
430*b7c941bbSAndroid Build Coastguard Worker             / sampleRate;
431*b7c941bbSAndroid Build Coastguard Worker 
432*b7c941bbSAndroid Build Coastguard Worker     // Latency is the difference in time between when a frame was recorded and
433*b7c941bbSAndroid Build Coastguard Worker     // when its corresponding echo was played.
434*b7c941bbSAndroid Build Coastguard Worker     return (mappedTimeNanos - inputTimeNanos) * 1.0e-6; // convert nanos to millis
435*b7c941bbSAndroid Build Coastguard Worker }