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 }