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