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