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