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 
dataCallbackProc(void * audioData,int32_t numFrames)58 aaudio_data_callback_result_t NativeAudioAnalyzer::dataCallbackProc(
59         void *audioData,
60         int32_t numFrames
61 ) {
62     aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE;
63     float  *outputData = (float  *) audioData;
64 
65     // Read audio data from the input stream.
66     int32_t actualFramesRead;
67 
68     if (numFrames > mInputFramesMaximum) {
69         ALOGE("%s() numFrames:%d > mInputFramesMaximum:%d", __func__, numFrames, mInputFramesMaximum);
70         mInputError = AAUDIO_ERROR_OUT_OF_RANGE;
71         return AAUDIO_CALLBACK_RESULT_STOP;
72     }
73 
74     if (numFrames > mMaxNumFrames) {
75         mMaxNumFrames = numFrames;
76     }
77     if (numFrames < mMinNumFrames) {
78         mMinNumFrames = numFrames;
79     }
80 
81     // Silence the output.
82     int32_t numBytes = numFrames * mActualOutputChannelCount * sizeof(float);
83     memset(audioData, 0 /* value */, numBytes);
84 
85     if (mNumCallbacksToDrain > 0) {
86         // Drain the input FIFOs.
87         int32_t totalFramesRead = 0;
88         do {
89             actualFramesRead = readFormattedData(numFrames);
90             if (actualFramesRead > 0) {
91                 totalFramesRead += actualFramesRead;
92             } else if (actualFramesRead < 0) {
93                 callbackResult = AAUDIO_CALLBACK_RESULT_STOP;
94             }
95             // Ignore errors because input stream may not be started yet.
96         } while (actualFramesRead > 0);
97         // Only counts if we actually got some data.
98         if (totalFramesRead > 0) {
99             mNumCallbacksToDrain--;
100         }
101 
102     } else if (mNumCallbacksToNotRead > 0) {
103         // Let the input fill up a bit so we are not so close to the write pointer.
104         mNumCallbacksToNotRead--;
105     } else if (mNumCallbacksToDiscard > 0) {
106         // Ignore. Allow the input to fill back up to equilibrium with the output.
107         actualFramesRead = readFormattedData(numFrames);
108         if (actualFramesRead < 0) {
109             callbackResult = AAUDIO_CALLBACK_RESULT_STOP;
110         }
111         mNumCallbacksToDiscard--;
112 
113     } else {
114         // The full duplex stream is now stable so process the audio.
115         int32_t numInputBytes = numFrames * mActualInputChannelCount * sizeof(float);
116         memset(mInputFloatData, 0 /* value */, numInputBytes);
117 
118         int64_t inputFramesWritten = AAudioStream_getFramesWritten(mInputStream);
119         int64_t inputFramesRead = AAudioStream_getFramesRead(mInputStream);
120         int64_t framesAvailable = inputFramesWritten - inputFramesRead;
121 
122         // Read the INPUT data.
123         actualFramesRead = readFormattedData(numFrames); // READ
124         if (actualFramesRead < 0) {
125             callbackResult = AAUDIO_CALLBACK_RESULT_STOP;
126         } else {
127             if (actualFramesRead < numFrames) {
128                 if(actualFramesRead < (int32_t) framesAvailable) {
129                     ALOGE("insufficient for no reason, numFrames = %d"
130                                    ", actualFramesRead = %d"
131                                    ", inputFramesWritten = %d"
132                                    ", inputFramesRead = %d"
133                                    ", available = %d\n",
134                            numFrames,
135                            actualFramesRead,
136                            (int) inputFramesWritten,
137                            (int) inputFramesRead,
138                            (int) framesAvailable);
139                 }
140                 mInsufficientReadCount++;
141                 mInsufficientReadFrames += numFrames - actualFramesRead; // deficit
142                 // ALOGE("Error insufficientReadCount = %d\n",(int)mInsufficientReadCount);
143             }
144 
145             int32_t numSamples = actualFramesRead * mActualInputChannelCount;
146 
147             if (mActualInputFormat == AAUDIO_FORMAT_PCM_I16) {
148                 convertPcm16ToFloat(mInputShortData, mInputFloatData, numSamples);
149             }
150 
151             // Process the INPUT and generate the OUTPUT.
152             mLoopbackProcessor->process(mInputFloatData,
153                                                mActualInputChannelCount,
154                                                numFrames,
155                                                outputData,
156                                                mActualOutputChannelCount,
157                                                numFrames);
158 
159             mIsDone = mLoopbackProcessor->isDone();
160             if (mIsDone) {
161                 callbackResult = AAUDIO_CALLBACK_RESULT_STOP;
162             }
163         }
164     }
165     mFramesWrittenTotal += numFrames;
166 
167     return callbackResult;
168 }
169 
s_MyDataCallbackProc(AAudioStream *,void * userData,void * audioData,int32_t numFrames)170 static aaudio_data_callback_result_t s_MyDataCallbackProc(
171         AAudioStream * /* outputStream */,
172         void *userData,
173         void *audioData,
174         int32_t numFrames) {
175     NativeAudioAnalyzer *myData = (NativeAudioAnalyzer *) userData;
176     return myData->dataCallbackProc(audioData, numFrames);
177 }
178 
s_MyErrorCallbackProc(AAudioStream *,void * userData,aaudio_result_t error)179 static void s_MyErrorCallbackProc(
180         AAudioStream * /* stream */,
181         void * userData,
182         aaudio_result_t error) {
183     ALOGE("Error Callback, error: %d\n",(int)error);
184     NativeAudioAnalyzer *myData = (NativeAudioAnalyzer *) userData;
185     myData->mOutputError = error;
186 }
187 
isRecordingComplete()188 bool NativeAudioAnalyzer::isRecordingComplete() {
189     return mPulseLatencyAnalyzer.isRecordingComplete();
190 }
191 
analyze()192 int NativeAudioAnalyzer::analyze() {
193     mPulseLatencyAnalyzer.analyze();
194     return getError(); // TODO review
195 }
196 
getLatencyMillis()197 double NativeAudioAnalyzer::getLatencyMillis() {
198     return mPulseLatencyAnalyzer.getMeasuredLatency() * 1000.0 / 48000;
199 }
200 
getConfidence()201 double NativeAudioAnalyzer::getConfidence() {
202     return mPulseLatencyAnalyzer.getMeasuredConfidence();
203 }
204 
isLowLatencyStream()205 bool NativeAudioAnalyzer::isLowLatencyStream() {
206     return mIsLowLatencyStream;
207 }
208 
getSampleRate()209 int NativeAudioAnalyzer::getSampleRate() {
210     return mOutputSampleRate;
211 }
212 
openAudio()213 aaudio_result_t NativeAudioAnalyzer::openAudio() {
214     AAudioStreamBuilder *builder = nullptr;
215 
216     mLoopbackProcessor = &mPulseLatencyAnalyzer; // for latency test
217 
218     // Use an AAudioStreamBuilder to contain requested parameters.
219     aaudio_result_t result = AAudio_createStreamBuilder(&builder);
220     if (result != AAUDIO_OK) {
221         ALOGE("AAudio_createStreamBuilder() returned %s",
222                AAudio_convertResultToText(result));
223         return result;
224     }
225 
226     // Create the OUTPUT stream -----------------------
227     AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
228     AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
229     AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
230     AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
231     AAudioStreamBuilder_setChannelCount(builder, 2); // stereo
232     AAudioStreamBuilder_setDataCallback(builder, s_MyDataCallbackProc, this);
233     AAudioStreamBuilder_setErrorCallback(builder, s_MyErrorCallbackProc, this);
234 
235     result = AAudioStreamBuilder_openStream(builder, &mOutputStream);
236     if (result != AAUDIO_OK) {
237         ALOGE("NativeAudioAnalyzer::openAudio() OUTPUT error %s",
238                AAudio_convertResultToText(result));
239         return result;
240     }
241 
242     // Did we get a low-latency stream?
243     mIsLowLatencyStream =
244         AAudioStream_getPerformanceMode(mOutputStream) == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
245 
246     int32_t outputFramesPerBurst = AAudioStream_getFramesPerBurst(mOutputStream);
247     (void) AAudioStream_setBufferSizeInFrames(mOutputStream, outputFramesPerBurst * kDefaultOutputSizeBursts);
248 
249     mOutputSampleRate = AAudioStream_getSampleRate(mOutputStream);
250     mActualOutputChannelCount = AAudioStream_getChannelCount(mOutputStream);
251 
252     // Create the INPUT stream -----------------------
253     AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
254     AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_UNSPECIFIED);
255     AAudioStreamBuilder_setSampleRate(builder, mOutputSampleRate); // must match
256     AAudioStreamBuilder_setChannelCount(builder, 1); // mono
257     AAudioStreamBuilder_setDataCallback(builder, nullptr, nullptr);
258     AAudioStreamBuilder_setErrorCallback(builder, nullptr, nullptr);
259     result = AAudioStreamBuilder_openStream(builder, &mInputStream);
260     if (result != AAUDIO_OK) {
261         ALOGE("NativeAudioAnalyzer::openAudio() INPUT error %s",
262                AAudio_convertResultToText(result));
263         return result;
264     }
265 
266     int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(mInputStream);
267     (void) AAudioStream_setBufferSizeInFrames(mInputStream, actualCapacity);
268 
269     // ------- Setup loopbackData -----------------------------
270     mActualInputFormat = AAudioStream_getFormat(mInputStream);
271     mActualInputChannelCount = AAudioStream_getChannelCount(mInputStream);
272 
273     // Allocate a buffer for the audio data.
274     mInputFramesMaximum = 32 * AAudioStream_getFramesPerBurst(mInputStream);
275 
276     if (mActualInputFormat == AAUDIO_FORMAT_PCM_I16) {
277         mInputShortData = new int16_t[mInputFramesMaximum * mActualInputChannelCount]{};
278     }
279     mInputFloatData = new float[mInputFramesMaximum * mActualInputChannelCount]{};
280 
281     return result;
282 }
283 
startAudio()284 aaudio_result_t NativeAudioAnalyzer::startAudio() {
285     mLoopbackProcessor->prepareToTest();
286 
287     // Start OUTPUT first so INPUT does not overflow.
288     aaudio_result_t result = AAudioStream_requestStart(mOutputStream);
289     if (result != AAUDIO_OK) {
290         stopAudio();
291         return result;
292     }
293 
294     result = AAudioStream_requestStart(mInputStream);
295     if (result != AAUDIO_OK) {
296         stopAudio();
297         return result;
298     }
299 
300     return result;
301 }
302 
stopAudio()303 aaudio_result_t NativeAudioAnalyzer::stopAudio() {
304     aaudio_result_t result1 = AAUDIO_OK;
305     aaudio_result_t result2 = AAUDIO_OK;
306     ALOGD("stopAudio() , minNumFrames = %d, maxNumFrames = %d\n", mMinNumFrames, mMaxNumFrames);
307     // Stop OUTPUT first because it uses INPUT.
308     if (mOutputStream != nullptr) {
309         result1 = AAudioStream_requestStop(mOutputStream);
310     }
311 
312     // Stop INPUT.
313     if (mInputStream != nullptr) {
314         result2 = AAudioStream_requestStop(mInputStream);
315     }
316     return result1 != AAUDIO_OK ? result1 : result2;
317 }
318 
closeAudio()319 aaudio_result_t NativeAudioAnalyzer::closeAudio() {
320     aaudio_result_t result1 = AAUDIO_OK;
321     aaudio_result_t result2 = AAUDIO_OK;
322     // Stop and close OUTPUT first because it uses INPUT.
323     if (mOutputStream != nullptr) {
324         result1 = AAudioStream_close(mOutputStream);
325         mOutputStream = nullptr;
326     }
327 
328     // Stop and close INPUT.
329     if (mInputStream != nullptr) {
330         result2 = AAudioStream_close(mInputStream);
331         mInputStream = nullptr;
332     }
333     return result1 != AAUDIO_OK ? result1 : result2;
334 }
335