1*b7c941bbSAndroid Build Coastguard Worker /*
2*b7c941bbSAndroid Build Coastguard Worker * Copyright 2020 The Android Open Source Project
3*b7c941bbSAndroid Build Coastguard Worker *
4*b7c941bbSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*b7c941bbSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*b7c941bbSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*b7c941bbSAndroid Build Coastguard Worker *
8*b7c941bbSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*b7c941bbSAndroid Build Coastguard Worker *
10*b7c941bbSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*b7c941bbSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*b7c941bbSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*b7c941bbSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*b7c941bbSAndroid Build Coastguard Worker * limitations under the License.
15*b7c941bbSAndroid Build Coastguard Worker */
16*b7c941bbSAndroid Build Coastguard Worker
17*b7c941bbSAndroid Build Coastguard Worker #include "NativeAudioAnalyzer.h"
18*b7c941bbSAndroid Build Coastguard Worker
19*b7c941bbSAndroid Build Coastguard Worker // AAudioStream_isMMapUsed() isn't a public symbol, so use dlsym() to access it.
20*b7c941bbSAndroid Build Coastguard Worker #include <dlfcn.h>
21*b7c941bbSAndroid Build Coastguard Worker #define LIB_AAUDIO_NAME "libaaudio.so"
22*b7c941bbSAndroid Build Coastguard Worker #define FUNCTION_IS_MMAP "AAudioStream_isMMapUsed"
23*b7c941bbSAndroid Build Coastguard Worker static bool (*aaudioStream_isMMap)(AAudioStream *stream) = nullptr;
24*b7c941bbSAndroid Build Coastguard Worker
25*b7c941bbSAndroid Build Coastguard Worker #include "WavFileCapture.h"
26*b7c941bbSAndroid Build Coastguard Worker
27*b7c941bbSAndroid Build Coastguard Worker extern WavFileCapture sWavFileCapture;
28*b7c941bbSAndroid Build Coastguard Worker
convertPcm16ToFloat(const int16_t * source,float * destination,int32_t numSamples)29*b7c941bbSAndroid Build Coastguard Worker static void convertPcm16ToFloat(const int16_t *source,
30*b7c941bbSAndroid Build Coastguard Worker float *destination,
31*b7c941bbSAndroid Build Coastguard Worker int32_t numSamples) {
32*b7c941bbSAndroid Build Coastguard Worker constexpr float scaler = 1.0f / 32768.0f;
33*b7c941bbSAndroid Build Coastguard Worker for (int i = 0; i < numSamples; i++) {
34*b7c941bbSAndroid Build Coastguard Worker destination[i] = source[i] * scaler;
35*b7c941bbSAndroid Build Coastguard Worker }
36*b7c941bbSAndroid Build Coastguard Worker }
37*b7c941bbSAndroid Build Coastguard Worker
38*b7c941bbSAndroid Build Coastguard Worker // Fill the audio output buffer.
readFormattedData(int32_t numFrames)39*b7c941bbSAndroid Build Coastguard Worker int32_t NativeAudioAnalyzer::readFormattedData(int32_t numFrames) {
40*b7c941bbSAndroid Build Coastguard Worker int32_t framesRead = AAUDIO_ERROR_INVALID_FORMAT;
41*b7c941bbSAndroid Build Coastguard Worker if (mActualInputFormat == AAUDIO_FORMAT_PCM_I16) {
42*b7c941bbSAndroid Build Coastguard Worker framesRead = AAudioStream_read(mInputStream, mInputShortData,
43*b7c941bbSAndroid Build Coastguard Worker numFrames,
44*b7c941bbSAndroid Build Coastguard Worker 0 /* timeoutNanoseconds */);
45*b7c941bbSAndroid Build Coastguard Worker } else if (mActualInputFormat == AAUDIO_FORMAT_PCM_FLOAT) {
46*b7c941bbSAndroid Build Coastguard Worker framesRead = AAudioStream_read(mInputStream, mInputFloatData,
47*b7c941bbSAndroid Build Coastguard Worker numFrames,
48*b7c941bbSAndroid Build Coastguard Worker 0 /* timeoutNanoseconds */);
49*b7c941bbSAndroid Build Coastguard Worker } else {
50*b7c941bbSAndroid Build Coastguard Worker ALOGE("ERROR actualInputFormat = %d\n", mActualInputFormat);
51*b7c941bbSAndroid Build Coastguard Worker assert(false);
52*b7c941bbSAndroid Build Coastguard Worker }
53*b7c941bbSAndroid Build Coastguard Worker if (framesRead < 0) {
54*b7c941bbSAndroid Build Coastguard Worker // Expect INVALID_STATE if STATE_STARTING
55*b7c941bbSAndroid Build Coastguard Worker if (mFramesReadTotal > 0) {
56*b7c941bbSAndroid Build Coastguard Worker mInputError = framesRead;
57*b7c941bbSAndroid Build Coastguard Worker ALOGE("ERROR in read = %d = %s\n", framesRead,
58*b7c941bbSAndroid Build Coastguard Worker AAudio_convertResultToText(framesRead));
59*b7c941bbSAndroid Build Coastguard Worker } else {
60*b7c941bbSAndroid Build Coastguard Worker framesRead = 0;
61*b7c941bbSAndroid Build Coastguard Worker }
62*b7c941bbSAndroid Build Coastguard Worker } else {
63*b7c941bbSAndroid Build Coastguard Worker mFramesReadTotal += framesRead;
64*b7c941bbSAndroid Build Coastguard Worker }
65*b7c941bbSAndroid Build Coastguard Worker return framesRead;
66*b7c941bbSAndroid Build Coastguard Worker }
67*b7c941bbSAndroid Build Coastguard Worker
has24BitSupport(aaudio_format_t format)68*b7c941bbSAndroid Build Coastguard Worker bool NativeAudioAnalyzer::has24BitSupport(aaudio_format_t format) {
69*b7c941bbSAndroid Build Coastguard Worker return (format == AAUDIO_FORMAT_PCM_FLOAT) || (format == AAUDIO_FORMAT_PCM_I24_PACKED)
70*b7c941bbSAndroid Build Coastguard Worker || (format == AAUDIO_FORMAT_PCM_I32);
71*b7c941bbSAndroid Build Coastguard Worker }
72*b7c941bbSAndroid Build Coastguard Worker
dataCallbackProc(void * audioData,int32_t numFrames)73*b7c941bbSAndroid Build Coastguard Worker aaudio_data_callback_result_t NativeAudioAnalyzer::dataCallbackProc(
74*b7c941bbSAndroid Build Coastguard Worker void *audioData,
75*b7c941bbSAndroid Build Coastguard Worker int32_t numFrames
76*b7c941bbSAndroid Build Coastguard Worker ) {
77*b7c941bbSAndroid Build Coastguard Worker aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE;
78*b7c941bbSAndroid Build Coastguard Worker float *outputData = (float *) audioData;
79*b7c941bbSAndroid Build Coastguard Worker
80*b7c941bbSAndroid Build Coastguard Worker // Read audio data from the input stream.
81*b7c941bbSAndroid Build Coastguard Worker int32_t actualFramesRead;
82*b7c941bbSAndroid Build Coastguard Worker
83*b7c941bbSAndroid Build Coastguard Worker if (numFrames > mInputFramesMaximum) {
84*b7c941bbSAndroid Build Coastguard Worker ALOGE("%s() numFrames:%d > mInputFramesMaximum:%d", __func__, numFrames, mInputFramesMaximum);
85*b7c941bbSAndroid Build Coastguard Worker mInputError = AAUDIO_ERROR_OUT_OF_RANGE;
86*b7c941bbSAndroid Build Coastguard Worker return AAUDIO_CALLBACK_RESULT_STOP;
87*b7c941bbSAndroid Build Coastguard Worker }
88*b7c941bbSAndroid Build Coastguard Worker
89*b7c941bbSAndroid Build Coastguard Worker if (numFrames > mMaxNumFrames) {
90*b7c941bbSAndroid Build Coastguard Worker mMaxNumFrames = numFrames;
91*b7c941bbSAndroid Build Coastguard Worker }
92*b7c941bbSAndroid Build Coastguard Worker if (numFrames < mMinNumFrames) {
93*b7c941bbSAndroid Build Coastguard Worker mMinNumFrames = numFrames;
94*b7c941bbSAndroid Build Coastguard Worker }
95*b7c941bbSAndroid Build Coastguard Worker
96*b7c941bbSAndroid Build Coastguard Worker // Get atomic snapshot of the relative frame positions so they
97*b7c941bbSAndroid Build Coastguard Worker // can be used to calculate timestamp latency.
98*b7c941bbSAndroid Build Coastguard Worker int64_t framesRead = AAudioStream_getFramesRead(mInputStream);
99*b7c941bbSAndroid Build Coastguard Worker int64_t framesWritten = AAudioStream_getFramesWritten(mOutputStream);
100*b7c941bbSAndroid Build Coastguard Worker mWriteReadDelta = framesWritten - framesRead;
101*b7c941bbSAndroid Build Coastguard Worker mWriteReadDeltaValid = true;
102*b7c941bbSAndroid Build Coastguard Worker
103*b7c941bbSAndroid Build Coastguard Worker // Silence the output.
104*b7c941bbSAndroid Build Coastguard Worker int32_t numBytes = numFrames * mActualOutputChannelCount * sizeof(float);
105*b7c941bbSAndroid Build Coastguard Worker memset(audioData, 0 /* value */, numBytes);
106*b7c941bbSAndroid Build Coastguard Worker
107*b7c941bbSAndroid Build Coastguard Worker if (mNumCallbacksToDrain > 0) {
108*b7c941bbSAndroid Build Coastguard Worker // Drain the input FIFOs.
109*b7c941bbSAndroid Build Coastguard Worker int32_t totalFramesRead = 0;
110*b7c941bbSAndroid Build Coastguard Worker do {
111*b7c941bbSAndroid Build Coastguard Worker actualFramesRead = readFormattedData(numFrames);
112*b7c941bbSAndroid Build Coastguard Worker if (actualFramesRead > 0) {
113*b7c941bbSAndroid Build Coastguard Worker totalFramesRead += actualFramesRead;
114*b7c941bbSAndroid Build Coastguard Worker } else if (actualFramesRead < 0) {
115*b7c941bbSAndroid Build Coastguard Worker callbackResult = AAUDIO_CALLBACK_RESULT_STOP;
116*b7c941bbSAndroid Build Coastguard Worker }
117*b7c941bbSAndroid Build Coastguard Worker // Ignore errors because input stream may not be started yet.
118*b7c941bbSAndroid Build Coastguard Worker } while (actualFramesRead > 0);
119*b7c941bbSAndroid Build Coastguard Worker // Only counts if we actually got some data.
120*b7c941bbSAndroid Build Coastguard Worker if (totalFramesRead > 0) {
121*b7c941bbSAndroid Build Coastguard Worker mNumCallbacksToDrain--;
122*b7c941bbSAndroid Build Coastguard Worker }
123*b7c941bbSAndroid Build Coastguard Worker
124*b7c941bbSAndroid Build Coastguard Worker } else if (mNumCallbacksToNotRead > 0) {
125*b7c941bbSAndroid Build Coastguard Worker // Let the input fill up a bit so we are not so close to the write pointer.
126*b7c941bbSAndroid Build Coastguard Worker mNumCallbacksToNotRead--;
127*b7c941bbSAndroid Build Coastguard Worker } else if (mNumCallbacksToDiscard > 0) {
128*b7c941bbSAndroid Build Coastguard Worker // Ignore. Allow the input to fill back up to equilibrium with the output.
129*b7c941bbSAndroid Build Coastguard Worker actualFramesRead = readFormattedData(numFrames);
130*b7c941bbSAndroid Build Coastguard Worker if (actualFramesRead < 0) {
131*b7c941bbSAndroid Build Coastguard Worker callbackResult = AAUDIO_CALLBACK_RESULT_STOP;
132*b7c941bbSAndroid Build Coastguard Worker }
133*b7c941bbSAndroid Build Coastguard Worker mNumCallbacksToDiscard--;
134*b7c941bbSAndroid Build Coastguard Worker
135*b7c941bbSAndroid Build Coastguard Worker } else {
136*b7c941bbSAndroid Build Coastguard Worker // The full duplex stream is now stable so process the audio.
137*b7c941bbSAndroid Build Coastguard Worker int32_t numInputBytes = numFrames * mActualInputChannelCount * sizeof(float);
138*b7c941bbSAndroid Build Coastguard Worker memset(mInputFloatData, 0 /* value */, numInputBytes);
139*b7c941bbSAndroid Build Coastguard Worker
140*b7c941bbSAndroid Build Coastguard Worker int64_t inputFramesWritten = AAudioStream_getFramesWritten(mInputStream);
141*b7c941bbSAndroid Build Coastguard Worker int64_t inputFramesRead = AAudioStream_getFramesRead(mInputStream);
142*b7c941bbSAndroid Build Coastguard Worker int64_t framesAvailable = inputFramesWritten - inputFramesRead;
143*b7c941bbSAndroid Build Coastguard Worker
144*b7c941bbSAndroid Build Coastguard Worker // Read the INPUT data.
145*b7c941bbSAndroid Build Coastguard Worker actualFramesRead = readFormattedData(numFrames); // READ
146*b7c941bbSAndroid Build Coastguard Worker if (actualFramesRead < 0) {
147*b7c941bbSAndroid Build Coastguard Worker callbackResult = AAUDIO_CALLBACK_RESULT_STOP;
148*b7c941bbSAndroid Build Coastguard Worker } else {
149*b7c941bbSAndroid Build Coastguard Worker if (actualFramesRead < numFrames) {
150*b7c941bbSAndroid Build Coastguard Worker if(actualFramesRead < (int32_t) framesAvailable) {
151*b7c941bbSAndroid Build Coastguard Worker ALOGE("insufficient for no reason, numFrames = %d"
152*b7c941bbSAndroid Build Coastguard Worker ", actualFramesRead = %d"
153*b7c941bbSAndroid Build Coastguard Worker ", inputFramesWritten = %d"
154*b7c941bbSAndroid Build Coastguard Worker ", inputFramesRead = %d"
155*b7c941bbSAndroid Build Coastguard Worker ", available = %d\n",
156*b7c941bbSAndroid Build Coastguard Worker numFrames,
157*b7c941bbSAndroid Build Coastguard Worker actualFramesRead,
158*b7c941bbSAndroid Build Coastguard Worker (int) inputFramesWritten,
159*b7c941bbSAndroid Build Coastguard Worker (int) inputFramesRead,
160*b7c941bbSAndroid Build Coastguard Worker (int) framesAvailable);
161*b7c941bbSAndroid Build Coastguard Worker }
162*b7c941bbSAndroid Build Coastguard Worker mInsufficientReadCount++;
163*b7c941bbSAndroid Build Coastguard Worker mInsufficientReadFrames += numFrames - actualFramesRead; // deficit
164*b7c941bbSAndroid Build Coastguard Worker // ALOGE("Error insufficientReadCount = %d\n",(int)mInsufficientReadCount);
165*b7c941bbSAndroid Build Coastguard Worker }
166*b7c941bbSAndroid Build Coastguard Worker
167*b7c941bbSAndroid Build Coastguard Worker int32_t numSamples = actualFramesRead * mActualInputChannelCount;
168*b7c941bbSAndroid Build Coastguard Worker
169*b7c941bbSAndroid Build Coastguard Worker if (mActualInputFormat == AAUDIO_FORMAT_PCM_I16) {
170*b7c941bbSAndroid Build Coastguard Worker convertPcm16ToFloat(mInputShortData, mInputFloatData, numSamples);
171*b7c941bbSAndroid Build Coastguard Worker }
172*b7c941bbSAndroid Build Coastguard Worker
173*b7c941bbSAndroid Build Coastguard Worker // Process the INPUT and generate the OUTPUT.
174*b7c941bbSAndroid Build Coastguard Worker mLoopbackProcessor->process(mInputFloatData,
175*b7c941bbSAndroid Build Coastguard Worker mActualInputChannelCount,
176*b7c941bbSAndroid Build Coastguard Worker numFrames,
177*b7c941bbSAndroid Build Coastguard Worker outputData,
178*b7c941bbSAndroid Build Coastguard Worker mActualOutputChannelCount,
179*b7c941bbSAndroid Build Coastguard Worker numFrames);
180*b7c941bbSAndroid Build Coastguard Worker
181*b7c941bbSAndroid Build Coastguard Worker sWavFileCapture.captureData(audioData, numFrames);
182*b7c941bbSAndroid Build Coastguard Worker
183*b7c941bbSAndroid Build Coastguard Worker mIsDone = mLoopbackProcessor->isDone();
184*b7c941bbSAndroid Build Coastguard Worker if (mIsDone) {
185*b7c941bbSAndroid Build Coastguard Worker callbackResult = AAUDIO_CALLBACK_RESULT_STOP;
186*b7c941bbSAndroid Build Coastguard Worker }
187*b7c941bbSAndroid Build Coastguard Worker }
188*b7c941bbSAndroid Build Coastguard Worker }
189*b7c941bbSAndroid Build Coastguard Worker mFramesWrittenTotal += numFrames;
190*b7c941bbSAndroid Build Coastguard Worker
191*b7c941bbSAndroid Build Coastguard Worker return callbackResult;
192*b7c941bbSAndroid Build Coastguard Worker }
193*b7c941bbSAndroid Build Coastguard Worker
s_MyDataCallbackProc(AAudioStream *,void * userData,void * audioData,int32_t numFrames)194*b7c941bbSAndroid Build Coastguard Worker static aaudio_data_callback_result_t s_MyDataCallbackProc(
195*b7c941bbSAndroid Build Coastguard Worker AAudioStream * /* outputStream */,
196*b7c941bbSAndroid Build Coastguard Worker void *userData,
197*b7c941bbSAndroid Build Coastguard Worker void *audioData,
198*b7c941bbSAndroid Build Coastguard Worker int32_t numFrames) {
199*b7c941bbSAndroid Build Coastguard Worker NativeAudioAnalyzer *myData = (NativeAudioAnalyzer *) userData;
200*b7c941bbSAndroid Build Coastguard Worker return myData->dataCallbackProc(audioData, numFrames);
201*b7c941bbSAndroid Build Coastguard Worker }
202*b7c941bbSAndroid Build Coastguard Worker
s_MyErrorCallbackProc(AAudioStream *,void * userData,aaudio_result_t error)203*b7c941bbSAndroid Build Coastguard Worker static void s_MyErrorCallbackProc(
204*b7c941bbSAndroid Build Coastguard Worker AAudioStream * /* stream */,
205*b7c941bbSAndroid Build Coastguard Worker void * userData,
206*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t error) {
207*b7c941bbSAndroid Build Coastguard Worker ALOGE("Error Callback, error: %d\n",(int)error);
208*b7c941bbSAndroid Build Coastguard Worker NativeAudioAnalyzer *myData = (NativeAudioAnalyzer *) userData;
209*b7c941bbSAndroid Build Coastguard Worker myData->mOutputError = error;
210*b7c941bbSAndroid Build Coastguard Worker }
211*b7c941bbSAndroid Build Coastguard Worker
isRecordingComplete()212*b7c941bbSAndroid Build Coastguard Worker bool NativeAudioAnalyzer::isRecordingComplete() {
213*b7c941bbSAndroid Build Coastguard Worker return mWhiteNoiseLatencyAnalyzer.isRecordingComplete();
214*b7c941bbSAndroid Build Coastguard Worker }
215*b7c941bbSAndroid Build Coastguard Worker
analyze()216*b7c941bbSAndroid Build Coastguard Worker int NativeAudioAnalyzer::analyze() {
217*b7c941bbSAndroid Build Coastguard Worker mWhiteNoiseLatencyAnalyzer.analyze();
218*b7c941bbSAndroid Build Coastguard Worker return getError(); // TODO review
219*b7c941bbSAndroid Build Coastguard Worker }
220*b7c941bbSAndroid Build Coastguard Worker
getLatencyMillis()221*b7c941bbSAndroid Build Coastguard Worker double NativeAudioAnalyzer::getLatencyMillis() {
222*b7c941bbSAndroid Build Coastguard Worker return mWhiteNoiseLatencyAnalyzer.getMeasuredLatency() * 1000.0 / 48000;
223*b7c941bbSAndroid Build Coastguard Worker }
224*b7c941bbSAndroid Build Coastguard Worker
getConfidence()225*b7c941bbSAndroid Build Coastguard Worker double NativeAudioAnalyzer::getConfidence() {
226*b7c941bbSAndroid Build Coastguard Worker return mWhiteNoiseLatencyAnalyzer.getMeasuredConfidence();
227*b7c941bbSAndroid Build Coastguard Worker }
228*b7c941bbSAndroid Build Coastguard Worker
has24BitHardwareSupport()229*b7c941bbSAndroid Build Coastguard Worker bool NativeAudioAnalyzer::has24BitHardwareSupport() {
230*b7c941bbSAndroid Build Coastguard Worker return mHas24BitHardwareSupport;
231*b7c941bbSAndroid Build Coastguard Worker }
232*b7c941bbSAndroid Build Coastguard Worker
getHardwareFormat()233*b7c941bbSAndroid Build Coastguard Worker int NativeAudioAnalyzer::getHardwareFormat() {
234*b7c941bbSAndroid Build Coastguard Worker return mHardwareFormat;
235*b7c941bbSAndroid Build Coastguard Worker }
236*b7c941bbSAndroid Build Coastguard Worker
getSampleRate()237*b7c941bbSAndroid Build Coastguard Worker int NativeAudioAnalyzer::getSampleRate() {
238*b7c941bbSAndroid Build Coastguard Worker return mOutputSampleRate;
239*b7c941bbSAndroid Build Coastguard Worker }
240*b7c941bbSAndroid Build Coastguard Worker
openAudio(int inputDeviceId,int outputDeviceId)241*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t NativeAudioAnalyzer::openAudio(int inputDeviceId, int outputDeviceId) {
242*b7c941bbSAndroid Build Coastguard Worker mInputDeviceId = inputDeviceId;
243*b7c941bbSAndroid Build Coastguard Worker mOutputDeviceId = outputDeviceId;
244*b7c941bbSAndroid Build Coastguard Worker
245*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder *builder = nullptr;
246*b7c941bbSAndroid Build Coastguard Worker
247*b7c941bbSAndroid Build Coastguard Worker mWhiteNoiseLatencyAnalyzer.setup();
248*b7c941bbSAndroid Build Coastguard Worker mLoopbackProcessor = &mWhiteNoiseLatencyAnalyzer; // for latency test
249*b7c941bbSAndroid Build Coastguard Worker
250*b7c941bbSAndroid Build Coastguard Worker // Use an AAudioStreamBuilder to contain requested parameters.
251*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t result = AAudio_createStreamBuilder(&builder);
252*b7c941bbSAndroid Build Coastguard Worker if (result != AAUDIO_OK) {
253*b7c941bbSAndroid Build Coastguard Worker ALOGE("AAudio_createStreamBuilder() returned %s",
254*b7c941bbSAndroid Build Coastguard Worker AAudio_convertResultToText(result));
255*b7c941bbSAndroid Build Coastguard Worker return result;
256*b7c941bbSAndroid Build Coastguard Worker }
257*b7c941bbSAndroid Build Coastguard Worker
258*b7c941bbSAndroid Build Coastguard Worker // Create the OUTPUT stream -----------------------
259*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
260*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
261*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
262*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
263*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setChannelCount(builder, 2); // stereo
264*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setDataCallback(builder, s_MyDataCallbackProc, this);
265*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setErrorCallback(builder, s_MyErrorCallbackProc, this);
266*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setDeviceId(builder, mOutputDeviceId);
267*b7c941bbSAndroid Build Coastguard Worker
268*b7c941bbSAndroid Build Coastguard Worker result = AAudioStreamBuilder_openStream(builder, &mOutputStream);
269*b7c941bbSAndroid Build Coastguard Worker if (result != AAUDIO_OK) {
270*b7c941bbSAndroid Build Coastguard Worker ALOGE("NativeAudioAnalyzer::openAudio() OUTPUT error %s",
271*b7c941bbSAndroid Build Coastguard Worker AAudio_convertResultToText(result));
272*b7c941bbSAndroid Build Coastguard Worker return result;
273*b7c941bbSAndroid Build Coastguard Worker }
274*b7c941bbSAndroid Build Coastguard Worker
275*b7c941bbSAndroid Build Coastguard Worker mHardwareFormat = AAudioStream_getHardwareFormat(mOutputStream);
276*b7c941bbSAndroid Build Coastguard Worker mHas24BitHardwareSupport = has24BitSupport(mHardwareFormat);
277*b7c941bbSAndroid Build Coastguard Worker
278*b7c941bbSAndroid Build Coastguard Worker // Stream Attributes
279*b7c941bbSAndroid Build Coastguard Worker mBurstFrames[STREAM_OUTPUT] = AAudioStream_getFramesPerBurst(mOutputStream);
280*b7c941bbSAndroid Build Coastguard Worker (void) AAudioStream_setBufferSizeInFrames(mOutputStream,
281*b7c941bbSAndroid Build Coastguard Worker mBurstFrames[STREAM_OUTPUT] * kDefaultOutputSizeBursts);
282*b7c941bbSAndroid Build Coastguard Worker mCapacityFrames[STREAM_OUTPUT] = AAudioStream_getBufferCapacityInFrames(mOutputStream);
283*b7c941bbSAndroid Build Coastguard Worker mIsLowLatencyStream[STREAM_OUTPUT] =
284*b7c941bbSAndroid Build Coastguard Worker AAudioStream_getPerformanceMode(mOutputStream) == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
285*b7c941bbSAndroid Build Coastguard Worker
286*b7c941bbSAndroid Build Coastguard Worker // Late binding for aaudioStream_isMMap()
287*b7c941bbSAndroid Build Coastguard Worker if (aaudioStream_isMMap == nullptr) {
288*b7c941bbSAndroid Build Coastguard Worker void* libHandle = dlopen(LIB_AAUDIO_NAME, RTLD_NOW);
289*b7c941bbSAndroid Build Coastguard Worker aaudioStream_isMMap = (bool (*)(AAudioStream *stream))
290*b7c941bbSAndroid Build Coastguard Worker dlsym(libHandle, FUNCTION_IS_MMAP);
291*b7c941bbSAndroid Build Coastguard Worker if (aaudioStream_isMMap == nullptr) {
292*b7c941bbSAndroid Build Coastguard Worker ALOGE("%s() could not find " FUNCTION_IS_MMAP, __func__);
293*b7c941bbSAndroid Build Coastguard Worker }
294*b7c941bbSAndroid Build Coastguard Worker }
295*b7c941bbSAndroid Build Coastguard Worker mIsMMap[STREAM_OUTPUT] =
296*b7c941bbSAndroid Build Coastguard Worker aaudioStream_isMMap != nullptr ? aaudioStream_isMMap(mOutputStream) : false;
297*b7c941bbSAndroid Build Coastguard Worker
298*b7c941bbSAndroid Build Coastguard Worker mOutputSampleRate = AAudioStream_getSampleRate(mOutputStream);
299*b7c941bbSAndroid Build Coastguard Worker mActualOutputChannelCount = AAudioStream_getChannelCount(mOutputStream);
300*b7c941bbSAndroid Build Coastguard Worker
301*b7c941bbSAndroid Build Coastguard Worker // Create the INPUT stream -----------------------
302*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
303*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_UNSPECIFIED);
304*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setSampleRate(builder, mOutputSampleRate); // must match
305*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setChannelCount(builder, 1); // mono
306*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setDataCallback(builder, nullptr, nullptr);
307*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setErrorCallback(builder, nullptr, nullptr);
308*b7c941bbSAndroid Build Coastguard Worker AAudioStreamBuilder_setDeviceId(builder, mInputDeviceId);
309*b7c941bbSAndroid Build Coastguard Worker
310*b7c941bbSAndroid Build Coastguard Worker result = AAudioStreamBuilder_openStream(builder, &mInputStream);
311*b7c941bbSAndroid Build Coastguard Worker if (result != AAUDIO_OK) {
312*b7c941bbSAndroid Build Coastguard Worker ALOGE("NativeAudioAnalyzer::openAudio() INPUT error %s",
313*b7c941bbSAndroid Build Coastguard Worker AAudio_convertResultToText(result));
314*b7c941bbSAndroid Build Coastguard Worker return result;
315*b7c941bbSAndroid Build Coastguard Worker }
316*b7c941bbSAndroid Build Coastguard Worker
317*b7c941bbSAndroid Build Coastguard Worker // Stream Attributes
318*b7c941bbSAndroid Build Coastguard Worker mIsLowLatencyStream[STREAM_INPUT] =
319*b7c941bbSAndroid Build Coastguard Worker AAudioStream_getPerformanceMode(mInputStream) == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
320*b7c941bbSAndroid Build Coastguard Worker mBurstFrames[STREAM_INPUT] = AAudioStream_getFramesPerBurst(mInputStream);
321*b7c941bbSAndroid Build Coastguard Worker mCapacityFrames[STREAM_INPUT] = AAudioStream_getBufferCapacityInFrames(mInputStream);
322*b7c941bbSAndroid Build Coastguard Worker mIsMMap[STREAM_INPUT] =
323*b7c941bbSAndroid Build Coastguard Worker aaudioStream_isMMap != nullptr ? aaudioStream_isMMap(mInputStream) : false;
324*b7c941bbSAndroid Build Coastguard Worker int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(mInputStream);
325*b7c941bbSAndroid Build Coastguard Worker (void) AAudioStream_setBufferSizeInFrames(mInputStream, actualCapacity);
326*b7c941bbSAndroid Build Coastguard Worker
327*b7c941bbSAndroid Build Coastguard Worker // ------- Setup loopbackData -----------------------------
328*b7c941bbSAndroid Build Coastguard Worker mActualInputFormat = AAudioStream_getFormat(mInputStream);
329*b7c941bbSAndroid Build Coastguard Worker mActualInputChannelCount = AAudioStream_getChannelCount(mInputStream);
330*b7c941bbSAndroid Build Coastguard Worker
331*b7c941bbSAndroid Build Coastguard Worker // Allocate a buffer for the audio data.
332*b7c941bbSAndroid Build Coastguard Worker mInputFramesMaximum = 32 * AAudioStream_getFramesPerBurst(mInputStream);
333*b7c941bbSAndroid Build Coastguard Worker
334*b7c941bbSAndroid Build Coastguard Worker if (mActualInputFormat == AAUDIO_FORMAT_PCM_I16) {
335*b7c941bbSAndroid Build Coastguard Worker mInputShortData = new int16_t[mInputFramesMaximum * mActualInputChannelCount]{};
336*b7c941bbSAndroid Build Coastguard Worker }
337*b7c941bbSAndroid Build Coastguard Worker mInputFloatData = new float[mInputFramesMaximum * mActualInputChannelCount]{};
338*b7c941bbSAndroid Build Coastguard Worker
339*b7c941bbSAndroid Build Coastguard Worker return result;
340*b7c941bbSAndroid Build Coastguard Worker }
341*b7c941bbSAndroid Build Coastguard Worker
startAudio()342*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t NativeAudioAnalyzer::startAudio() {
343*b7c941bbSAndroid Build Coastguard Worker mLoopbackProcessor->prepareToTest();
344*b7c941bbSAndroid Build Coastguard Worker
345*b7c941bbSAndroid Build Coastguard Worker mWriteReadDeltaValid = false;
346*b7c941bbSAndroid Build Coastguard Worker
347*b7c941bbSAndroid Build Coastguard Worker // Start OUTPUT first so INPUT does not overflow.
348*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t result = AAudioStream_requestStart(mOutputStream);
349*b7c941bbSAndroid Build Coastguard Worker if (result != AAUDIO_OK) {
350*b7c941bbSAndroid Build Coastguard Worker stopAudio();
351*b7c941bbSAndroid Build Coastguard Worker return result;
352*b7c941bbSAndroid Build Coastguard Worker }
353*b7c941bbSAndroid Build Coastguard Worker
354*b7c941bbSAndroid Build Coastguard Worker result = AAudioStream_requestStart(mInputStream);
355*b7c941bbSAndroid Build Coastguard Worker if (result != AAUDIO_OK) {
356*b7c941bbSAndroid Build Coastguard Worker stopAudio();
357*b7c941bbSAndroid Build Coastguard Worker return result;
358*b7c941bbSAndroid Build Coastguard Worker }
359*b7c941bbSAndroid Build Coastguard Worker
360*b7c941bbSAndroid Build Coastguard Worker return result;
361*b7c941bbSAndroid Build Coastguard Worker }
362*b7c941bbSAndroid Build Coastguard Worker
stopAudio()363*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t NativeAudioAnalyzer::stopAudio() {
364*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t result1 = AAUDIO_OK;
365*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t result2 = AAUDIO_OK;
366*b7c941bbSAndroid Build Coastguard Worker ALOGD("stopAudio() , minNumFrames = %d, maxNumFrames = %d\n", mMinNumFrames, mMaxNumFrames);
367*b7c941bbSAndroid Build Coastguard Worker // Stop OUTPUT first because it uses INPUT.
368*b7c941bbSAndroid Build Coastguard Worker if (mOutputStream != nullptr) {
369*b7c941bbSAndroid Build Coastguard Worker result1 = AAudioStream_requestStop(mOutputStream);
370*b7c941bbSAndroid Build Coastguard Worker }
371*b7c941bbSAndroid Build Coastguard Worker
372*b7c941bbSAndroid Build Coastguard Worker // Stop INPUT.
373*b7c941bbSAndroid Build Coastguard Worker if (mInputStream != nullptr) {
374*b7c941bbSAndroid Build Coastguard Worker result2 = AAudioStream_requestStop(mInputStream);
375*b7c941bbSAndroid Build Coastguard Worker }
376*b7c941bbSAndroid Build Coastguard Worker return result1 != AAUDIO_OK ? result1 : result2;
377*b7c941bbSAndroid Build Coastguard Worker }
378*b7c941bbSAndroid Build Coastguard Worker
closeAudio()379*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t NativeAudioAnalyzer::closeAudio() {
380*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t result1 = AAUDIO_OK;
381*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t result2 = AAUDIO_OK;
382*b7c941bbSAndroid Build Coastguard Worker // Stop and close OUTPUT first because it uses INPUT.
383*b7c941bbSAndroid Build Coastguard Worker if (mOutputStream != nullptr) {
384*b7c941bbSAndroid Build Coastguard Worker result1 = AAudioStream_close(mOutputStream);
385*b7c941bbSAndroid Build Coastguard Worker mOutputStream = nullptr;
386*b7c941bbSAndroid Build Coastguard Worker }
387*b7c941bbSAndroid Build Coastguard Worker
388*b7c941bbSAndroid Build Coastguard Worker // Stop and close INPUT.
389*b7c941bbSAndroid Build Coastguard Worker if (mInputStream != nullptr) {
390*b7c941bbSAndroid Build Coastguard Worker result2 = AAudioStream_close(mInputStream);
391*b7c941bbSAndroid Build Coastguard Worker mInputStream = nullptr;
392*b7c941bbSAndroid Build Coastguard Worker }
393*b7c941bbSAndroid Build Coastguard Worker return result1 != AAUDIO_OK ? result1 : result2;
394*b7c941bbSAndroid Build Coastguard Worker }
395*b7c941bbSAndroid Build Coastguard Worker
396*b7c941bbSAndroid Build Coastguard Worker // The timestamp latency is the difference between the input
397*b7c941bbSAndroid Build Coastguard Worker // and output times for a specific frame.
398*b7c941bbSAndroid Build Coastguard Worker // Start with the position and time from an input timestamp.
399*b7c941bbSAndroid Build Coastguard Worker // Map the input position to the corresponding position in output
400*b7c941bbSAndroid Build Coastguard Worker // and calculate its time.
401*b7c941bbSAndroid Build Coastguard Worker // Use the difference between framesWritten and framesRead to
402*b7c941bbSAndroid Build Coastguard Worker // convert input positions to output positions.
403*b7c941bbSAndroid Build Coastguard Worker // Returns -1.0 if the data callback wasn't called or if the stream is closed.
measureTimestampLatencyMillis()404*b7c941bbSAndroid Build Coastguard Worker double NativeAudioAnalyzer::measureTimestampLatencyMillis() {
405*b7c941bbSAndroid Build Coastguard Worker if (!mWriteReadDeltaValid) return -1.0; // Data callback never called.
406*b7c941bbSAndroid Build Coastguard Worker
407*b7c941bbSAndroid Build Coastguard Worker int64_t writeReadDelta = mWriteReadDelta;
408*b7c941bbSAndroid Build Coastguard Worker aaudio_result_t result;
409*b7c941bbSAndroid Build Coastguard Worker
410*b7c941bbSAndroid Build Coastguard Worker int64_t inputPosition;
411*b7c941bbSAndroid Build Coastguard Worker int64_t inputTimeNanos;
412*b7c941bbSAndroid Build Coastguard Worker int64_t outputPosition;
413*b7c941bbSAndroid Build Coastguard Worker int64_t outputTimeNanos;
414*b7c941bbSAndroid Build Coastguard Worker result = AAudioStream_getTimestamp(mInputStream, CLOCK_MONOTONIC, &inputPosition,
415*b7c941bbSAndroid Build Coastguard Worker &inputTimeNanos);
416*b7c941bbSAndroid Build Coastguard Worker if (result != AAUDIO_OK) {
417*b7c941bbSAndroid Build Coastguard Worker return -1.0; // Stream is closed.
418*b7c941bbSAndroid Build Coastguard Worker }
419*b7c941bbSAndroid Build Coastguard Worker result = AAudioStream_getTimestamp(mOutputStream, CLOCK_MONOTONIC, &outputPosition,
420*b7c941bbSAndroid Build Coastguard Worker &outputTimeNanos);
421*b7c941bbSAndroid Build Coastguard Worker if (result != AAUDIO_OK) {
422*b7c941bbSAndroid Build Coastguard Worker return -1.0; // Stream is closed.
423*b7c941bbSAndroid Build Coastguard Worker }
424*b7c941bbSAndroid Build Coastguard Worker
425*b7c941bbSAndroid Build Coastguard Worker // Map input frame position to the corresponding output frame.
426*b7c941bbSAndroid Build Coastguard Worker int64_t mappedPosition = inputPosition + writeReadDelta;
427*b7c941bbSAndroid Build Coastguard Worker // Calculate when that frame will play.
428*b7c941bbSAndroid Build Coastguard Worker int32_t sampleRate = getSampleRate();
429*b7c941bbSAndroid Build Coastguard Worker int64_t mappedTimeNanos = outputTimeNanos + ((mappedPosition - outputPosition) * 1e9)
430*b7c941bbSAndroid Build Coastguard Worker / sampleRate;
431*b7c941bbSAndroid Build Coastguard Worker
432*b7c941bbSAndroid Build Coastguard Worker // Latency is the difference in time between when a frame was recorded and
433*b7c941bbSAndroid Build Coastguard Worker // when its corresponding echo was played.
434*b7c941bbSAndroid Build Coastguard Worker return (mappedTimeNanos - inputTimeNanos) * 1.0e-6; // convert nanos to millis
435*b7c941bbSAndroid Build Coastguard Worker }