Home | History | Annotate | Download | only in src
      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