14485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk/* 24485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * Copyright (C) 2016 The Android Open Source Project 34485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * 44485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * Licensed under the Apache License, Version 2.0 (the "License"); 54485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * you may not use this file except in compliance with the License. 64485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * You may obtain a copy of the License at 74485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * 84485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * http://www.apache.org/licenses/LICENSE-2.0 94485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * 104485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * Unless required by applicable law or agreed to in writing, software 114485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * distributed under the License is distributed on an "AS IS" BASIS, 124485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * See the License for the specific language governing permissions and 144485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * limitations under the License. 154485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk */ 164485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 174485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// Play an impulse and then record it. 184485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// Measure the round trip latency. 194485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 204485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <assert.h> 214485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <cctype> 224485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <math.h> 234485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <stdlib.h> 244485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <stdio.h> 254485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <stdlib.h> 264485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <unistd.h> 274485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 284485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <aaudio/AAudio.h> 294485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 304485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define INPUT_PEAK_THRESHOLD 0.1f 314485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define SILENCE_FRAMES 10000 324485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define SAMPLE_RATE 48000 334485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define NUM_SECONDS 7 344485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define FILENAME "/data/oboe_input.raw" 354485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 364485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define NANOS_PER_MICROSECOND ((int64_t)1000) 374485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000) 384485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define MILLIS_PER_SECOND 1000 394485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * MILLIS_PER_SECOND) 404485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 414485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkclass AudioRecorder 424485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk{ 434485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkpublic: 444485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AudioRecorder() { 454485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 464485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk ~AudioRecorder() { 474485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk delete[] mData; 484485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 494485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 504485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk void allocate(int maxFrames) { 514485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk delete[] mData; 524485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mData = new float[maxFrames]; 534485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mMaxFrames = maxFrames; 544485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 554485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 564485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk void record(int16_t *inputData, int inputChannelCount, int numFrames) { 574485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // stop at end of buffer 584485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if ((mFrameCounter + numFrames) > mMaxFrames) { 594485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk numFrames = mMaxFrames - mFrameCounter; 604485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 614485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk for (int i = 0; i < numFrames; i++) { 624485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768); 634485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 644485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 654485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 664485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk void record(float *inputData, int inputChannelCount, int numFrames) { 674485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // stop at end of buffer 684485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if ((mFrameCounter + numFrames) > mMaxFrames) { 694485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk numFrames = mMaxFrames - mFrameCounter; 704485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 714485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk for (int i = 0; i < numFrames; i++) { 724485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mData[mFrameCounter++] = inputData[i * inputChannelCount]; 734485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 744485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 754485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 764485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int save(const char *fileName) { 774485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk FILE *fid = fopen(fileName, "wb"); 784485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (fid == NULL) { 794485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk return errno; 804485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 814485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int written = fwrite(mData, sizeof(float), mFrameCounter, fid); 824485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk fclose(fid); 834485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk return written; 844485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 854485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 864485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkprivate: 874485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk float *mData = NULL; 884485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t mFrameCounter = 0; 894485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t mMaxFrames = 0; 904485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk}; 914485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 924485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ==================================================================================== 934485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ========================= Loopback Processor ======================================= 944485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ==================================================================================== 954485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkclass LoopbackProcessor { 964485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkpublic: 974485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 984485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Calculate mean and standard deviation. 994485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk double calculateAverageLatency(double *deviation) { 1004485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (mLatencyCount <= 0) { 1014485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk return -1.0; 1024485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1034485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk double sum = 0.0; 1044485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk for (int i = 0; i < mLatencyCount; i++) { 1054485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk sum += mLatencyArray[i]; 1064485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1074485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk double average = sum / mLatencyCount; 1084485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk sum = 0.0; 1094485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk for (int i = 0; i < mLatencyCount; i++) { 1104485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk double error = average - mLatencyArray[i]; 1114485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk sum += error * error; // squared 1124485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1134485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk *deviation = sqrt(sum / mLatencyCount); 1144485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk return average; 1154485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1164485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 1174485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk float getMaxAmplitude() const { return mMaxAmplitude; } 1184485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int getMeasurementCount() const { return mLatencyCount; } 1194485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk float getAverageAmplitude() const { return mAmplitudeTotal / mAmplitudeCount; } 1204485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 1214485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // TODO Convert this to a feedback circuit and then use auto-correlation to measure the period. 1224485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk void process(float *inputData, int inputChannelCount, 1234485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk float *outputData, int outputChannelCount, 1244485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int numFrames) { 1254485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk (void) outputChannelCount; 1264485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 1274485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Measure peak and average amplitude. 1284485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk for (int i = 0; i < numFrames; i++) { 1294485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk float sample = inputData[i * inputChannelCount]; 1304485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (sample > mMaxAmplitude) { 1314485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mMaxAmplitude = sample; 1324485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1334485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (sample < 0) { 1344485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk sample = 0 - sample; 1354485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1364485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mAmplitudeTotal += sample; 1374485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mAmplitudeCount++; 1384485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1394485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 1404485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Clear output. 1414485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk memset(outputData, 0, numFrames * outputChannelCount * sizeof(float)); 1424485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 1434485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Wait a while between hearing the pulse and starting a new one. 1444485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (mState == STATE_SILENT) { 1454485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mCounter += numFrames; 1464485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (mCounter > SILENCE_FRAMES) { 1474485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk //printf("LoopbackProcessor send impulse, burst #%d\n", mBurstCounter); 1484485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // copy impulse 1494485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk for (float sample : mImpulse) { 1504485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk *outputData = sample; 1514485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk outputData += outputChannelCount; 1524485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1534485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mState = STATE_LISTENING; 1544485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mCounter = 0; 1554485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1564485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1574485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Start listening as soon as we send the impulse. 1584485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (mState == STATE_LISTENING) { 1594485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk for (int i = 0; i < numFrames; i++) { 1604485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk float sample = inputData[i * inputChannelCount]; 1614485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (sample >= INPUT_PEAK_THRESHOLD) { 1624485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mLatencyArray[mLatencyCount++] = mCounter; 1634485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (mLatencyCount >= MAX_LATENCY_VALUES) { 1644485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mState = STATE_DONE; 1654485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } else { 1664485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mState = STATE_SILENT; 1674485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1684485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mCounter = 0; 1694485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk break; 1704485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } else { 1714485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mCounter++; 1724485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1734485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1744485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1754485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1764485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 1774485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk void echo(float *inputData, int inputChannelCount, 1784485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk float *outputData, int outputChannelCount, 1794485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int numFrames) { 1804485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int channelsValid = (inputChannelCount < outputChannelCount) 1814485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk ? inputChannelCount : outputChannelCount; 1824485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk for (int i = 0; i < numFrames; i++) { 1834485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int ic; 1844485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk for (ic = 0; ic < channelsValid; ic++) { 1854485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk outputData[ic] = inputData[ic]; 1864485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1874485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk for (ic = 0; ic < outputChannelCount; ic++) { 1884485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk outputData[ic] = 0; 1894485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1904485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk inputData += inputChannelCount; 1914485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk outputData += outputChannelCount; 1924485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1934485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 1944485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkprivate: 1954485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk enum { 1964485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk STATE_SILENT, 1974485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk STATE_LISTENING, 1984485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk STATE_DONE 1994485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk }; 2004485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2014485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk enum { 2024485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk MAX_LATENCY_VALUES = 64 2034485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk }; 2044485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2054485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int mState = STATE_SILENT; 2064485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t mCounter = 0; 2074485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t mLatencyArray[MAX_LATENCY_VALUES]; 2084485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t mLatencyCount = 0; 2094485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk float mMaxAmplitude = 0; 2104485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk float mAmplitudeTotal = 0; 2114485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t mAmplitudeCount = 0; 2124485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk static const float mImpulse[5]; 2134485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk}; 2144485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2154485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkconst float LoopbackProcessor::mImpulse[5] = {0.5f, 0.9f, 0.0f, -0.9f, -0.5f}; 2164485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2174485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// TODO make this a class that manages its own buffer allocation 2184485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkstruct LoopbackData { 2194485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStream *inputStream = nullptr; 2204485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t inputFramesMaximum = 0; 2214485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int16_t *inputData = nullptr; 2224485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk float *conversionBuffer = nullptr; 2234485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t actualInputChannelCount = 0; 2244485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t actualOutputChannelCount = 0; 2254485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t inputBuffersToDiscard = 10; 2264485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2274485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk aaudio_result_t inputError; 2284485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk LoopbackProcessor loopbackProcessor; 2294485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AudioRecorder audioRecorder; 2304485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk}; 2314485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2324485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkstatic void convertPcm16ToFloat(const int16_t *source, 2334485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk float *destination, 2344485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t numSamples) { 2354485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk const float scaler = 1.0f / 32768.0f; 2364485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk for (int i = 0; i < numSamples; i++) { 2374485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk destination[i] = source[i] * scaler; 2384485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 2394485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk} 2404485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2414485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ==================================================================================== 2424485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ========================= CALLBACK ================================================= 2434485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ==================================================================================== 2444485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// Callback function that fills the audio output buffer. 2454485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkstatic aaudio_data_callback_result_t MyDataCallbackProc( 2464485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStream *outputStream, 2474485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk void *userData, 2484485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk void *audioData, 2494485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t numFrames 2504485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk) { 2514485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk (void) outputStream; 2524485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk LoopbackData *myData = (LoopbackData *) userData; 2534485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk float *outputData = (float *) audioData; 2544485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2554485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Read audio data from the input stream. 2564485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t framesRead; 2574485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2584485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (numFrames > myData->inputFramesMaximum) { 2594485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk myData->inputError = AAUDIO_ERROR_OUT_OF_RANGE; 2604485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk return AAUDIO_CALLBACK_RESULT_STOP; 2614485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 2624485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2634485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (myData->inputBuffersToDiscard > 0) { 2644485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Drain the input. 2654485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk do { 2664485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk framesRead = AAudioStream_read(myData->inputStream, myData->inputData, 2674485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk numFrames, 0); 2684485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (framesRead < 0) { 2694485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk myData->inputError = framesRead; 2704485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } else if (framesRead > 0) { 2714485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk myData->inputBuffersToDiscard--; 2724485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 2734485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } while(framesRead > 0); 2744485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } else { 2754485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk framesRead = AAudioStream_read(myData->inputStream, myData->inputData, 2764485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk numFrames, 0); 2774485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (framesRead < 0) { 2784485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk myData->inputError = framesRead; 2794485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } else if (framesRead > 0) { 2804485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Process valid input data. 2814485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk myData->audioRecorder.record(myData->inputData, 2824485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk myData->actualInputChannelCount, 2834485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk framesRead); 2844485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2854485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t numSamples = framesRead * myData->actualInputChannelCount; 2864485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk convertPcm16ToFloat(myData->inputData, myData->conversionBuffer, numSamples); 2874485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2884485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk myData->loopbackProcessor.process(myData->conversionBuffer, 2894485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk myData->actualInputChannelCount, 2904485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk outputData, 2914485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk myData->actualOutputChannelCount, 2924485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk framesRead); 2934485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 2944485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 2954485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2964485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk return AAUDIO_CALLBACK_RESULT_CONTINUE; 2974485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk} 2984485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 2994485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkstatic void usage() { 3004485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("loopback: -b{burstsPerBuffer} -p{outputPerfMode} -P{inputPerfMode}\n"); 3014485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" -b{burstsPerBuffer} for example 2 for double buffered\n"); 3024485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" -p{outputPerfMode} set output AAUDIO_PERFORMANCE_MODE*\n"); 3034485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" -P{inputPerfMode} set input AAUDIO_PERFORMANCE_MODE*\n"); 3044485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" n for _NONE\n"); 3054485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" l for _LATENCY\n"); 3064485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" p for _POWER_SAVING;\n"); 3074485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("For example: loopback -b2 -pl -Pn\n"); 3084485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk} 3094485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 3104485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkstatic aaudio_performance_mode_t parsePerformanceMode(char c) { 3114485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk aaudio_performance_mode_t mode = AAUDIO_PERFORMANCE_MODE_NONE; 3124485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk c = tolower(c); 3134485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk switch (c) { 3144485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk case 'n': 3154485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mode = AAUDIO_PERFORMANCE_MODE_NONE; 3164485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk break; 3174485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk case 'l': 3184485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; 3194485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk break; 3204485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk case 'p': 3214485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk mode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING; 3224485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk break; 3234485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk default: 3244485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("ERROR invalue performance mode %c\n", c); 3254485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk break; 3264485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 3274485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk return mode; 3284485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk} 3294485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 3304485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ==================================================================================== 3314485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// TODO break up this large main() function into smaller functions 3324485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkint main(int argc, const char **argv) 3334485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk{ 3344485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk aaudio_result_t result = AAUDIO_OK; 3354485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk LoopbackData loopbackData; 3364485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStream *outputStream = nullptr; 3374485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 3384485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk const int requestedInputChannelCount = 1; 3394485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk const int requestedOutputChannelCount = AAUDIO_UNSPECIFIED; 3404485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk const int requestedSampleRate = SAMPLE_RATE; 3414485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int actualSampleRate = 0; 3429dca9824da74d50be02bc81f539cc77b7bde678aPhil Burk const aaudio_format_t requestedInputFormat = AAUDIO_FORMAT_PCM_I16; 3439dca9824da74d50be02bc81f539cc77b7bde678aPhil Burk const aaudio_format_t requestedOutputFormat = AAUDIO_FORMAT_PCM_FLOAT; 3449dca9824da74d50be02bc81f539cc77b7bde678aPhil Burk aaudio_format_t actualInputFormat; 3459dca9824da74d50be02bc81f539cc77b7bde678aPhil Burk aaudio_format_t actualOutputFormat; 3464485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 34787c9f646a94259d7c321c3b3d5947fa1778f5ac2Phil Burk const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE; 34887c9f646a94259d7c321c3b3d5947fa1778f5ac2Phil Burk //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED; 3494485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk aaudio_sharing_mode_t actualSharingMode; 3504485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 3514485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStreamBuilder *builder = nullptr; 3524485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED; 3534485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t framesPerBurst = 0; 3544485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk float *outputData = NULL; 3554485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk double deviation; 3564485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk double latency; 3574485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk aaudio_performance_mode_t outputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; 3584485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; 3594485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 3604485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int32_t burstsPerBuffer = 1; // single buffered 3614485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 3624485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk for (int i = 1; i < argc; i++) { 3634485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk const char *arg = argv[i]; 3644485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (arg[0] == '-') { 3654485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk char option = arg[1]; 3664485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk switch (option) { 3674485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk case 'b': 3684485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk burstsPerBuffer = atoi(&arg[2]); 3694485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk break; 3704485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk case 'p': 3714485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk outputPerformanceLevel = parsePerformanceMode(arg[2]); 3724485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk break; 3734485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk case 'P': 3744485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk inputPerformanceLevel = parsePerformanceMode(arg[2]); 3754485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk break; 3764485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk default: 3774485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk usage(); 3784485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk break; 3794485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 3804485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } else { 3814485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk break; 3824485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 3834485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 3844485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 3854485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk loopbackData.audioRecorder.allocate(NUM_SECONDS * SAMPLE_RATE); 3864485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 3874485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Make printf print immediately so that debug info is not stuck 3884485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // in a buffer if we hang or crash. 3894485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk setvbuf(stdout, NULL, _IONBF, (size_t) 0); 3904485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 3914485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("%s - Audio loopback using AAudio\n", argv[0]); 3924485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 3934485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Use an AAudioStreamBuilder to contain requested parameters. 3944485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk result = AAudio_createStreamBuilder(&builder); 3954485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (result < 0) { 3964485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk goto finish; 3974485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 3984485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 3994485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Request common stream properties. 4004485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStreamBuilder_setSampleRate(builder, requestedSampleRate); 4014485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStreamBuilder_setFormat(builder, requestedInputFormat); 4024485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStreamBuilder_setSharingMode(builder, requestedSharingMode); 4034485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4044485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Open the input stream. 4054485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT); 4064485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStreamBuilder_setPerformanceMode(builder, inputPerformanceLevel); 4074485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStreamBuilder_setChannelCount(builder, requestedInputChannelCount); 4084485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4094485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk result = AAudioStreamBuilder_openStream(builder, &loopbackData.inputStream); 4104485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("AAudioStreamBuilder_openStream(input) returned %d = %s\n", 4114485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk result, AAudio_convertResultToText(result)); 4124485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (result < 0) { 4134485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk goto finish; 4144485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 4154485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4164485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Create an output stream using the Builder. 4174485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT); 4184485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStreamBuilder_setFormat(builder, requestedOutputFormat); 4194485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStreamBuilder_setPerformanceMode(builder, outputPerformanceLevel); 4204485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStreamBuilder_setChannelCount(builder, requestedOutputChannelCount); 4214485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStreamBuilder_setDataCallback(builder, MyDataCallbackProc, &loopbackData); 4224485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4234485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk result = AAudioStreamBuilder_openStream(builder, &outputStream); 4244485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("AAudioStreamBuilder_openStream(output) returned %d = %s\n", 4254485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk result, AAudio_convertResultToText(result)); 4264485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (result != AAUDIO_OK) { 4274485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk goto finish; 4284485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 4294485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4304485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("Stream INPUT ---------------------\n"); 4314485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk loopbackData.actualInputChannelCount = AAudioStream_getChannelCount(loopbackData.inputStream); 4324485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" channelCount: requested = %d, actual = %d\n", requestedInputChannelCount, 4334485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk loopbackData.actualInputChannelCount); 4344485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" framesPerBurst = %d\n", AAudioStream_getFramesPerBurst(loopbackData.inputStream)); 4354485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4364485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk actualInputFormat = AAudioStream_getFormat(loopbackData.inputStream); 4374485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" dataFormat: requested = %d, actual = %d\n", requestedInputFormat, actualInputFormat); 4384485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk assert(actualInputFormat == AAUDIO_FORMAT_PCM_I16); 4394485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4404485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("Stream OUTPUT ---------------------\n"); 4414485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Check to see what kind of stream we actually got. 4424485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk actualSampleRate = AAudioStream_getSampleRate(outputStream); 4434485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" sampleRate: requested = %d, actual = %d\n", requestedSampleRate, actualSampleRate); 4444485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4454485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk loopbackData.actualOutputChannelCount = AAudioStream_getChannelCount(outputStream); 4464485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" channelCount: requested = %d, actual = %d\n", requestedOutputChannelCount, 4474485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk loopbackData.actualOutputChannelCount); 4484485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4494485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk actualSharingMode = AAudioStream_getSharingMode(outputStream); 4504485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" sharingMode: requested = %d, actual = %d\n", requestedSharingMode, actualSharingMode); 4514485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4524485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // This is the number of frames that are read in one chunk by a DMA controller 4534485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // or a DSP or a mixer. 4544485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk framesPerBurst = AAudioStream_getFramesPerBurst(outputStream); 4554485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" framesPerBurst = %d\n", framesPerBurst); 4564485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4574485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" bufferCapacity = %d\n", AAudioStream_getBufferCapacityInFrames(outputStream)); 4584485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4594485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk actualOutputFormat = AAudioStream_getFormat(outputStream); 4604485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf(" dataFormat: requested = %d, actual = %d\n", requestedOutputFormat, actualOutputFormat); 4614485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk assert(actualOutputFormat == AAUDIO_FORMAT_PCM_FLOAT); 4624485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4634485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Allocate a buffer for the audio data. 4644485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk loopbackData.inputFramesMaximum = 32 * framesPerBurst; 4654485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4664485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk loopbackData.inputData = new int16_t[loopbackData.inputFramesMaximum * loopbackData.actualInputChannelCount]; 4674485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk loopbackData.conversionBuffer = new float[loopbackData.inputFramesMaximum * 4684485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk loopbackData.actualInputChannelCount]; 4694485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4704485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk result = AAudioStream_setBufferSizeInFrames(outputStream, burstsPerBuffer * framesPerBurst); 4714485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (result < 0) { // may be positive buffer size 4724485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk fprintf(stderr, "ERROR - AAudioStream_setBufferSize() returned %d\n", result); 4734485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk goto finish; 4744485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 4754485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("AAudioStream_setBufferSize() actual = %d\n",result); 4764485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4774485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk // Start output first so input stream runs low. 4784485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk result = AAudioStream_requestStart(outputStream); 4794485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (result != AAUDIO_OK) { 4804485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk fprintf(stderr, "ERROR - AAudioStream_requestStart(output) returned %d = %s\n", 4814485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk result, AAudio_convertResultToText(result)); 4824485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk goto finish; 4834485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 4844485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4854485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk result = AAudioStream_requestStart(loopbackData.inputStream); 4864485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk if (result != AAUDIO_OK) { 4874485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk fprintf(stderr, "ERROR - AAudioStream_requestStart(input) returned %d = %s\n", 4884485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk result, AAudio_convertResultToText(result)); 4894485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk goto finish; 4904485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 4914485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4924485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("------- sleep while the callback runs --------------\n"); 4934485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk fflush(stdout); 4944485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk sleep(NUM_SECONDS); 4954485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4964485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 4974485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("input error = %d = %s\n", 4984485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk loopbackData.inputError, AAudio_convertResultToText(loopbackData.inputError)); 4994485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 5004485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("AAudioStream_getXRunCount %d\n", AAudioStream_getXRunCount(outputStream)); 5014485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("framesRead = %d\n", (int) AAudioStream_getFramesRead(outputStream)); 5024485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("framesWritten = %d\n", (int) AAudioStream_getFramesWritten(outputStream)); 5034485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 5044485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk latency = loopbackData.loopbackProcessor.calculateAverageLatency(&deviation); 5054485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("measured peak = %8.5f\n", loopbackData.loopbackProcessor.getMaxAmplitude()); 5064485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("threshold = %8.5f\n", INPUT_PEAK_THRESHOLD); 5074485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("measured average = %8.5f\n", loopbackData.loopbackProcessor.getAverageAmplitude()); 5084485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("# latency measurements = %d\n", loopbackData.loopbackProcessor.getMeasurementCount()); 5094485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("measured latency = %8.2f +/- %4.5f frames\n", latency, deviation); 5104485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("measured latency = %8.2f msec <===== !!\n", (1000.0 * latency / actualSampleRate)); 5114485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 5124485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk { 5134485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk int written = loopbackData.audioRecorder.save(FILENAME); 5144485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("wrote %d samples to %s\n", written, FILENAME); 5154485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk } 5164485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 5174485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkfinish: 5184485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStream_close(outputStream); 5194485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStream_close(loopbackData.inputStream); 5204485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk delete[] loopbackData.conversionBuffer; 5214485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk delete[] loopbackData.inputData; 5224485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk delete[] outputData; 5234485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk AAudioStreamBuilder_delete(builder); 5244485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 5254485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result)); 5264485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS; 5274485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk} 5284485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk 529