1 /* 2 * Copyright (C) 2016 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 // Play an impulse and then record it. 18 // Measure the round trip latency. 19 20 #include <assert.h> 21 #include <cctype> 22 #include <math.h> 23 #include <stdlib.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <unistd.h> 27 28 #include <aaudio/AAudio.h> 29 30 #define INPUT_PEAK_THRESHOLD 0.1f 31 #define SILENCE_FRAMES 10000 32 #define SAMPLE_RATE 48000 33 #define NUM_SECONDS 7 34 #define FILENAME "/data/oboe_input.raw" 35 36 #define NANOS_PER_MICROSECOND ((int64_t)1000) 37 #define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000) 38 #define MILLIS_PER_SECOND 1000 39 #define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * MILLIS_PER_SECOND) 40 41 class AudioRecorder 42 { 43 public: 44 AudioRecorder() { 45 } 46 ~AudioRecorder() { 47 delete[] mData; 48 } 49 50 void allocate(int maxFrames) { 51 delete[] mData; 52 mData = new float[maxFrames]; 53 mMaxFrames = maxFrames; 54 } 55 56 void record(int16_t *inputData, int inputChannelCount, int numFrames) { 57 // stop at end of buffer 58 if ((mFrameCounter + numFrames) > mMaxFrames) { 59 numFrames = mMaxFrames - mFrameCounter; 60 } 61 for (int i = 0; i < numFrames; i++) { 62 mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768); 63 } 64 } 65 66 void record(float *inputData, int inputChannelCount, int numFrames) { 67 // stop at end of buffer 68 if ((mFrameCounter + numFrames) > mMaxFrames) { 69 numFrames = mMaxFrames - mFrameCounter; 70 } 71 for (int i = 0; i < numFrames; i++) { 72 mData[mFrameCounter++] = inputData[i * inputChannelCount]; 73 } 74 } 75 76 int save(const char *fileName) { 77 FILE *fid = fopen(fileName, "wb"); 78 if (fid == NULL) { 79 return errno; 80 } 81 int written = fwrite(mData, sizeof(float), mFrameCounter, fid); 82 fclose(fid); 83 return written; 84 } 85 86 private: 87 float *mData = NULL; 88 int32_t mFrameCounter = 0; 89 int32_t mMaxFrames = 0; 90 }; 91 92 // ==================================================================================== 93 // ========================= Loopback Processor ======================================= 94 // ==================================================================================== 95 class LoopbackProcessor { 96 public: 97 98 // Calculate mean and standard deviation. 99 double calculateAverageLatency(double *deviation) { 100 if (mLatencyCount <= 0) { 101 return -1.0; 102 } 103 double sum = 0.0; 104 for (int i = 0; i < mLatencyCount; i++) { 105 sum += mLatencyArray[i]; 106 } 107 double average = sum / mLatencyCount; 108 sum = 0.0; 109 for (int i = 0; i < mLatencyCount; i++) { 110 double error = average - mLatencyArray[i]; 111 sum += error * error; // squared 112 } 113 *deviation = sqrt(sum / mLatencyCount); 114 return average; 115 } 116 117 float getMaxAmplitude() const { return mMaxAmplitude; } 118 int getMeasurementCount() const { return mLatencyCount; } 119 float getAverageAmplitude() const { return mAmplitudeTotal / mAmplitudeCount; } 120 121 // TODO Convert this to a feedback circuit and then use auto-correlation to measure the period. 122 void process(float *inputData, int inputChannelCount, 123 float *outputData, int outputChannelCount, 124 int numFrames) { 125 (void) outputChannelCount; 126 127 // Measure peak and average amplitude. 128 for (int i = 0; i < numFrames; i++) { 129 float sample = inputData[i * inputChannelCount]; 130 if (sample > mMaxAmplitude) { 131 mMaxAmplitude = sample; 132 } 133 if (sample < 0) { 134 sample = 0 - sample; 135 } 136 mAmplitudeTotal += sample; 137 mAmplitudeCount++; 138 } 139 140 // Clear output. 141 memset(outputData, 0, numFrames * outputChannelCount * sizeof(float)); 142 143 // Wait a while between hearing the pulse and starting a new one. 144 if (mState == STATE_SILENT) { 145 mCounter += numFrames; 146 if (mCounter > SILENCE_FRAMES) { 147 //printf("LoopbackProcessor send impulse, burst #%d\n", mBurstCounter); 148 // copy impulse 149 for (float sample : mImpulse) { 150 *outputData = sample; 151 outputData += outputChannelCount; 152 } 153 mState = STATE_LISTENING; 154 mCounter = 0; 155 } 156 } 157 // Start listening as soon as we send the impulse. 158 if (mState == STATE_LISTENING) { 159 for (int i = 0; i < numFrames; i++) { 160 float sample = inputData[i * inputChannelCount]; 161 if (sample >= INPUT_PEAK_THRESHOLD) { 162 mLatencyArray[mLatencyCount++] = mCounter; 163 if (mLatencyCount >= MAX_LATENCY_VALUES) { 164 mState = STATE_DONE; 165 } else { 166 mState = STATE_SILENT; 167 } 168 mCounter = 0; 169 break; 170 } else { 171 mCounter++; 172 } 173 } 174 } 175 } 176 177 void echo(float *inputData, int inputChannelCount, 178 float *outputData, int outputChannelCount, 179 int numFrames) { 180 int channelsValid = (inputChannelCount < outputChannelCount) 181 ? inputChannelCount : outputChannelCount; 182 for (int i = 0; i < numFrames; i++) { 183 int ic; 184 for (ic = 0; ic < channelsValid; ic++) { 185 outputData[ic] = inputData[ic]; 186 } 187 for (ic = 0; ic < outputChannelCount; ic++) { 188 outputData[ic] = 0; 189 } 190 inputData += inputChannelCount; 191 outputData += outputChannelCount; 192 } 193 } 194 private: 195 enum { 196 STATE_SILENT, 197 STATE_LISTENING, 198 STATE_DONE 199 }; 200 201 enum { 202 MAX_LATENCY_VALUES = 64 203 }; 204 205 int mState = STATE_SILENT; 206 int32_t mCounter = 0; 207 int32_t mLatencyArray[MAX_LATENCY_VALUES]; 208 int32_t mLatencyCount = 0; 209 float mMaxAmplitude = 0; 210 float mAmplitudeTotal = 0; 211 int32_t mAmplitudeCount = 0; 212 static const float mImpulse[5]; 213 }; 214 215 const float LoopbackProcessor::mImpulse[5] = {0.5f, 0.9f, 0.0f, -0.9f, -0.5f}; 216 217 // TODO make this a class that manages its own buffer allocation 218 struct LoopbackData { 219 AAudioStream *inputStream = nullptr; 220 int32_t inputFramesMaximum = 0; 221 int16_t *inputData = nullptr; 222 float *conversionBuffer = nullptr; 223 int32_t actualInputChannelCount = 0; 224 int32_t actualOutputChannelCount = 0; 225 int32_t inputBuffersToDiscard = 10; 226 227 aaudio_result_t inputError; 228 LoopbackProcessor loopbackProcessor; 229 AudioRecorder audioRecorder; 230 }; 231 232 static void convertPcm16ToFloat(const int16_t *source, 233 float *destination, 234 int32_t numSamples) { 235 const float scaler = 1.0f / 32768.0f; 236 for (int i = 0; i < numSamples; i++) { 237 destination[i] = source[i] * scaler; 238 } 239 } 240 241 // ==================================================================================== 242 // ========================= CALLBACK ================================================= 243 // ==================================================================================== 244 // Callback function that fills the audio output buffer. 245 static aaudio_data_callback_result_t MyDataCallbackProc( 246 AAudioStream *outputStream, 247 void *userData, 248 void *audioData, 249 int32_t numFrames 250 ) { 251 (void) outputStream; 252 LoopbackData *myData = (LoopbackData *) userData; 253 float *outputData = (float *) audioData; 254 255 // Read audio data from the input stream. 256 int32_t framesRead; 257 258 if (numFrames > myData->inputFramesMaximum) { 259 myData->inputError = AAUDIO_ERROR_OUT_OF_RANGE; 260 return AAUDIO_CALLBACK_RESULT_STOP; 261 } 262 263 if (myData->inputBuffersToDiscard > 0) { 264 // Drain the input. 265 do { 266 framesRead = AAudioStream_read(myData->inputStream, myData->inputData, 267 numFrames, 0); 268 if (framesRead < 0) { 269 myData->inputError = framesRead; 270 } else if (framesRead > 0) { 271 myData->inputBuffersToDiscard--; 272 } 273 } while(framesRead > 0); 274 } else { 275 framesRead = AAudioStream_read(myData->inputStream, myData->inputData, 276 numFrames, 0); 277 if (framesRead < 0) { 278 myData->inputError = framesRead; 279 } else if (framesRead > 0) { 280 // Process valid input data. 281 myData->audioRecorder.record(myData->inputData, 282 myData->actualInputChannelCount, 283 framesRead); 284 285 int32_t numSamples = framesRead * myData->actualInputChannelCount; 286 convertPcm16ToFloat(myData->inputData, myData->conversionBuffer, numSamples); 287 288 myData->loopbackProcessor.process(myData->conversionBuffer, 289 myData->actualInputChannelCount, 290 outputData, 291 myData->actualOutputChannelCount, 292 framesRead); 293 } 294 } 295 296 return AAUDIO_CALLBACK_RESULT_CONTINUE; 297 } 298 299 static void usage() { 300 printf("loopback: -b{burstsPerBuffer} -p{outputPerfMode} -P{inputPerfMode}\n"); 301 printf(" -b{burstsPerBuffer} for example 2 for double buffered\n"); 302 printf(" -p{outputPerfMode} set output AAUDIO_PERFORMANCE_MODE*\n"); 303 printf(" -P{inputPerfMode} set input AAUDIO_PERFORMANCE_MODE*\n"); 304 printf(" n for _NONE\n"); 305 printf(" l for _LATENCY\n"); 306 printf(" p for _POWER_SAVING;\n"); 307 printf("For example: loopback -b2 -pl -Pn\n"); 308 } 309 310 static aaudio_performance_mode_t parsePerformanceMode(char c) { 311 aaudio_performance_mode_t mode = AAUDIO_PERFORMANCE_MODE_NONE; 312 c = tolower(c); 313 switch (c) { 314 case 'n': 315 mode = AAUDIO_PERFORMANCE_MODE_NONE; 316 break; 317 case 'l': 318 mode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; 319 break; 320 case 'p': 321 mode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING; 322 break; 323 default: 324 printf("ERROR invalue performance mode %c\n", c); 325 break; 326 } 327 return mode; 328 } 329 330 // ==================================================================================== 331 // TODO break up this large main() function into smaller functions 332 int main(int argc, const char **argv) 333 { 334 aaudio_result_t result = AAUDIO_OK; 335 LoopbackData loopbackData; 336 AAudioStream *outputStream = nullptr; 337 338 const int requestedInputChannelCount = 1; 339 const int requestedOutputChannelCount = AAUDIO_UNSPECIFIED; 340 const int requestedSampleRate = SAMPLE_RATE; 341 int actualSampleRate = 0; 342 const aaudio_format_t requestedInputFormat = AAUDIO_FORMAT_PCM_I16; 343 const aaudio_format_t requestedOutputFormat = AAUDIO_FORMAT_PCM_FLOAT; 344 aaudio_format_t actualInputFormat; 345 aaudio_format_t actualOutputFormat; 346 347 const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE; 348 //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED; 349 aaudio_sharing_mode_t actualSharingMode; 350 351 AAudioStreamBuilder *builder = nullptr; 352 aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED; 353 int32_t framesPerBurst = 0; 354 float *outputData = NULL; 355 double deviation; 356 double latency; 357 aaudio_performance_mode_t outputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; 358 aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; 359 360 int32_t burstsPerBuffer = 1; // single buffered 361 362 for (int i = 1; i < argc; i++) { 363 const char *arg = argv[i]; 364 if (arg[0] == '-') { 365 char option = arg[1]; 366 switch (option) { 367 case 'b': 368 burstsPerBuffer = atoi(&arg[2]); 369 break; 370 case 'p': 371 outputPerformanceLevel = parsePerformanceMode(arg[2]); 372 break; 373 case 'P': 374 inputPerformanceLevel = parsePerformanceMode(arg[2]); 375 break; 376 default: 377 usage(); 378 break; 379 } 380 } else { 381 break; 382 } 383 } 384 385 loopbackData.audioRecorder.allocate(NUM_SECONDS * SAMPLE_RATE); 386 387 // Make printf print immediately so that debug info is not stuck 388 // in a buffer if we hang or crash. 389 setvbuf(stdout, NULL, _IONBF, (size_t) 0); 390 391 printf("%s - Audio loopback using AAudio\n", argv[0]); 392 393 // Use an AAudioStreamBuilder to contain requested parameters. 394 result = AAudio_createStreamBuilder(&builder); 395 if (result < 0) { 396 goto finish; 397 } 398 399 // Request common stream properties. 400 AAudioStreamBuilder_setSampleRate(builder, requestedSampleRate); 401 AAudioStreamBuilder_setFormat(builder, requestedInputFormat); 402 AAudioStreamBuilder_setSharingMode(builder, requestedSharingMode); 403 404 // Open the input stream. 405 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT); 406 AAudioStreamBuilder_setPerformanceMode(builder, inputPerformanceLevel); 407 AAudioStreamBuilder_setChannelCount(builder, requestedInputChannelCount); 408 409 result = AAudioStreamBuilder_openStream(builder, &loopbackData.inputStream); 410 printf("AAudioStreamBuilder_openStream(input) returned %d = %s\n", 411 result, AAudio_convertResultToText(result)); 412 if (result < 0) { 413 goto finish; 414 } 415 416 // Create an output stream using the Builder. 417 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT); 418 AAudioStreamBuilder_setFormat(builder, requestedOutputFormat); 419 AAudioStreamBuilder_setPerformanceMode(builder, outputPerformanceLevel); 420 AAudioStreamBuilder_setChannelCount(builder, requestedOutputChannelCount); 421 AAudioStreamBuilder_setDataCallback(builder, MyDataCallbackProc, &loopbackData); 422 423 result = AAudioStreamBuilder_openStream(builder, &outputStream); 424 printf("AAudioStreamBuilder_openStream(output) returned %d = %s\n", 425 result, AAudio_convertResultToText(result)); 426 if (result != AAUDIO_OK) { 427 goto finish; 428 } 429 430 printf("Stream INPUT ---------------------\n"); 431 loopbackData.actualInputChannelCount = AAudioStream_getChannelCount(loopbackData.inputStream); 432 printf(" channelCount: requested = %d, actual = %d\n", requestedInputChannelCount, 433 loopbackData.actualInputChannelCount); 434 printf(" framesPerBurst = %d\n", AAudioStream_getFramesPerBurst(loopbackData.inputStream)); 435 436 actualInputFormat = AAudioStream_getFormat(loopbackData.inputStream); 437 printf(" dataFormat: requested = %d, actual = %d\n", requestedInputFormat, actualInputFormat); 438 assert(actualInputFormat == AAUDIO_FORMAT_PCM_I16); 439 440 printf("Stream OUTPUT ---------------------\n"); 441 // Check to see what kind of stream we actually got. 442 actualSampleRate = AAudioStream_getSampleRate(outputStream); 443 printf(" sampleRate: requested = %d, actual = %d\n", requestedSampleRate, actualSampleRate); 444 445 loopbackData.actualOutputChannelCount = AAudioStream_getChannelCount(outputStream); 446 printf(" channelCount: requested = %d, actual = %d\n", requestedOutputChannelCount, 447 loopbackData.actualOutputChannelCount); 448 449 actualSharingMode = AAudioStream_getSharingMode(outputStream); 450 printf(" sharingMode: requested = %d, actual = %d\n", requestedSharingMode, actualSharingMode); 451 452 // This is the number of frames that are read in one chunk by a DMA controller 453 // or a DSP or a mixer. 454 framesPerBurst = AAudioStream_getFramesPerBurst(outputStream); 455 printf(" framesPerBurst = %d\n", framesPerBurst); 456 457 printf(" bufferCapacity = %d\n", AAudioStream_getBufferCapacityInFrames(outputStream)); 458 459 actualOutputFormat = AAudioStream_getFormat(outputStream); 460 printf(" dataFormat: requested = %d, actual = %d\n", requestedOutputFormat, actualOutputFormat); 461 assert(actualOutputFormat == AAUDIO_FORMAT_PCM_FLOAT); 462 463 // Allocate a buffer for the audio data. 464 loopbackData.inputFramesMaximum = 32 * framesPerBurst; 465 466 loopbackData.inputData = new int16_t[loopbackData.inputFramesMaximum * loopbackData.actualInputChannelCount]; 467 loopbackData.conversionBuffer = new float[loopbackData.inputFramesMaximum * 468 loopbackData.actualInputChannelCount]; 469 470 result = AAudioStream_setBufferSizeInFrames(outputStream, burstsPerBuffer * framesPerBurst); 471 if (result < 0) { // may be positive buffer size 472 fprintf(stderr, "ERROR - AAudioStream_setBufferSize() returned %d\n", result); 473 goto finish; 474 } 475 printf("AAudioStream_setBufferSize() actual = %d\n",result); 476 477 // Start output first so input stream runs low. 478 result = AAudioStream_requestStart(outputStream); 479 if (result != AAUDIO_OK) { 480 fprintf(stderr, "ERROR - AAudioStream_requestStart(output) returned %d = %s\n", 481 result, AAudio_convertResultToText(result)); 482 goto finish; 483 } 484 485 result = AAudioStream_requestStart(loopbackData.inputStream); 486 if (result != AAUDIO_OK) { 487 fprintf(stderr, "ERROR - AAudioStream_requestStart(input) returned %d = %s\n", 488 result, AAudio_convertResultToText(result)); 489 goto finish; 490 } 491 492 printf("------- sleep while the callback runs --------------\n"); 493 fflush(stdout); 494 sleep(NUM_SECONDS); 495 496 497 printf("input error = %d = %s\n", 498 loopbackData.inputError, AAudio_convertResultToText(loopbackData.inputError)); 499 500 printf("AAudioStream_getXRunCount %d\n", AAudioStream_getXRunCount(outputStream)); 501 printf("framesRead = %d\n", (int) AAudioStream_getFramesRead(outputStream)); 502 printf("framesWritten = %d\n", (int) AAudioStream_getFramesWritten(outputStream)); 503 504 latency = loopbackData.loopbackProcessor.calculateAverageLatency(&deviation); 505 printf("measured peak = %8.5f\n", loopbackData.loopbackProcessor.getMaxAmplitude()); 506 printf("threshold = %8.5f\n", INPUT_PEAK_THRESHOLD); 507 printf("measured average = %8.5f\n", loopbackData.loopbackProcessor.getAverageAmplitude()); 508 printf("# latency measurements = %d\n", loopbackData.loopbackProcessor.getMeasurementCount()); 509 printf("measured latency = %8.2f +/- %4.5f frames\n", latency, deviation); 510 printf("measured latency = %8.2f msec <===== !!\n", (1000.0 * latency / actualSampleRate)); 511 512 { 513 int written = loopbackData.audioRecorder.save(FILENAME); 514 printf("wrote %d samples to %s\n", written, FILENAME); 515 } 516 517 finish: 518 AAudioStream_close(outputStream); 519 AAudioStream_close(loopbackData.inputStream); 520 delete[] loopbackData.conversionBuffer; 521 delete[] loopbackData.inputData; 522 delete[] outputData; 523 AAudioStreamBuilder_delete(builder); 524 525 printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result)); 526 return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS; 527 } 528 529