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