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 mWhiteNoiseLatencyAnalyzer.isRecordingComplete();
190 }
191
analyze()192 int NativeAudioAnalyzer::analyze() {
193 mWhiteNoiseLatencyAnalyzer.analyze();
194 return getError(); // TODO review
195 }
196
getLatencyMillis()197 double NativeAudioAnalyzer::getLatencyMillis() {
198 return mWhiteNoiseLatencyAnalyzer.getMeasuredLatency() * 1000.0 / 48000;
199 }
200
getConfidence()201 double NativeAudioAnalyzer::getConfidence() {
202 return mWhiteNoiseLatencyAnalyzer.getMeasuredConfidence();
203 }
204
isLowLatencyStream()205 bool NativeAudioAnalyzer::isLowLatencyStream() {
206 return mIsLowLatencyStream;
207 }
208
getSampleRate()209 int NativeAudioAnalyzer::getSampleRate() {
210 return mOutputSampleRate;
211 }
212
openAudio(int inputDeviceId,int outputDeviceId)213 aaudio_result_t NativeAudioAnalyzer::openAudio(int inputDeviceId, int outputDeviceId) {
214 mInputDeviceId = inputDeviceId;
215 mOutputDeviceId = outputDeviceId;
216
217 AAudioStreamBuilder *builder = nullptr;
218
219 mWhiteNoiseLatencyAnalyzer.setup();
220 mLoopbackProcessor = &mWhiteNoiseLatencyAnalyzer; // for latency test
221
222 // Use an AAudioStreamBuilder to contain requested parameters.
223 aaudio_result_t result = AAudio_createStreamBuilder(&builder);
224 if (result != AAUDIO_OK) {
225 ALOGE("AAudio_createStreamBuilder() returned %s",
226 AAudio_convertResultToText(result));
227 return result;
228 }
229
230 // Create the OUTPUT stream -----------------------
231 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
232 AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
233 AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
234 AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
235 AAudioStreamBuilder_setChannelCount(builder, 2); // stereo
236 AAudioStreamBuilder_setDataCallback(builder, s_MyDataCallbackProc, this);
237 AAudioStreamBuilder_setErrorCallback(builder, s_MyErrorCallbackProc, this);
238 AAudioStreamBuilder_setDeviceId(builder, mOutputDeviceId);
239
240 result = AAudioStreamBuilder_openStream(builder, &mOutputStream);
241 if (result != AAUDIO_OK) {
242 ALOGE("NativeAudioAnalyzer::openAudio() OUTPUT error %s",
243 AAudio_convertResultToText(result));
244 return result;
245 }
246
247 // Did we get a low-latency stream?
248 mIsLowLatencyStream =
249 AAudioStream_getPerformanceMode(mOutputStream) == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
250
251 int32_t outputFramesPerBurst = AAudioStream_getFramesPerBurst(mOutputStream);
252 (void) AAudioStream_setBufferSizeInFrames(mOutputStream, outputFramesPerBurst * kDefaultOutputSizeBursts);
253
254 mOutputSampleRate = AAudioStream_getSampleRate(mOutputStream);
255 mActualOutputChannelCount = AAudioStream_getChannelCount(mOutputStream);
256
257 // Create the INPUT stream -----------------------
258 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
259 AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_UNSPECIFIED);
260 AAudioStreamBuilder_setSampleRate(builder, mOutputSampleRate); // must match
261 AAudioStreamBuilder_setChannelCount(builder, 1); // mono
262 AAudioStreamBuilder_setDataCallback(builder, nullptr, nullptr);
263 AAudioStreamBuilder_setErrorCallback(builder, nullptr, nullptr);
264 AAudioStreamBuilder_setDeviceId(builder, mInputDeviceId);
265
266 result = AAudioStreamBuilder_openStream(builder, &mInputStream);
267 if (result != AAUDIO_OK) {
268 ALOGE("NativeAudioAnalyzer::openAudio() INPUT error %s",
269 AAudio_convertResultToText(result));
270 return result;
271 }
272
273 int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(mInputStream);
274 (void) AAudioStream_setBufferSizeInFrames(mInputStream, actualCapacity);
275
276 // ------- Setup loopbackData -----------------------------
277 mActualInputFormat = AAudioStream_getFormat(mInputStream);
278 mActualInputChannelCount = AAudioStream_getChannelCount(mInputStream);
279
280 // Allocate a buffer for the audio data.
281 mInputFramesMaximum = 32 * AAudioStream_getFramesPerBurst(mInputStream);
282
283 if (mActualInputFormat == AAUDIO_FORMAT_PCM_I16) {
284 mInputShortData = new int16_t[mInputFramesMaximum * mActualInputChannelCount]{};
285 }
286 mInputFloatData = new float[mInputFramesMaximum * mActualInputChannelCount]{};
287
288 return result;
289 }
290
startAudio()291 aaudio_result_t NativeAudioAnalyzer::startAudio() {
292 mLoopbackProcessor->prepareToTest();
293
294 // Start OUTPUT first so INPUT does not overflow.
295 aaudio_result_t result = AAudioStream_requestStart(mOutputStream);
296 if (result != AAUDIO_OK) {
297 stopAudio();
298 return result;
299 }
300
301 result = AAudioStream_requestStart(mInputStream);
302 if (result != AAUDIO_OK) {
303 stopAudio();
304 return result;
305 }
306
307 return result;
308 }
309
stopAudio()310 aaudio_result_t NativeAudioAnalyzer::stopAudio() {
311 aaudio_result_t result1 = AAUDIO_OK;
312 aaudio_result_t result2 = AAUDIO_OK;
313 ALOGD("stopAudio() , minNumFrames = %d, maxNumFrames = %d\n", mMinNumFrames, mMaxNumFrames);
314 // Stop OUTPUT first because it uses INPUT.
315 if (mOutputStream != nullptr) {
316 result1 = AAudioStream_requestStop(mOutputStream);
317 }
318
319 // Stop INPUT.
320 if (mInputStream != nullptr) {
321 result2 = AAudioStream_requestStop(mInputStream);
322 }
323 return result1 != AAUDIO_OK ? result1 : result2;
324 }
325
closeAudio()326 aaudio_result_t NativeAudioAnalyzer::closeAudio() {
327 aaudio_result_t result1 = AAUDIO_OK;
328 aaudio_result_t result2 = AAUDIO_OK;
329 // Stop and close OUTPUT first because it uses INPUT.
330 if (mOutputStream != nullptr) {
331 result1 = AAudioStream_close(mOutputStream);
332 mOutputStream = nullptr;
333 }
334
335 // Stop and close INPUT.
336 if (mInputStream != nullptr) {
337 result2 = AAudioStream_close(mInputStream);
338 mInputStream = nullptr;
339 }
340 return result1 != AAUDIO_OK ? result1 : result2;
341 }
342