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 }