/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // FIXME taken from OpenSLES_AndroidConfiguration.h #define SL_ANDROID_KEY_PERFORMANCE_MODE ((const SLchar*) "androidPerformanceMode") //////////////////////////////////////////// /// Actual sles functions. // Test program to record from default audio input and playback to default audio output. // It will generate feedback (Larsen effect) if played through on-device speakers, // or acts as a delay if played through headset. #define _USE_MATH_DEFINES #include #include "sles.h" #include "audio_utils/atomic.h" #include "byte_buffer.h" #include #include static int slesCreateServer(sles_data *pSles, int samplingRate, int frameCount, int micSource, int performanceMode, int testType, double frequency1, char* byteBufferPtr, int byteBufferLength, short* loopbackTone, int maxRecordedLateCallbacks, int ignoreFirstFrames); static int slesDestroyServer(sles_data *pSles); static void initBufferStats(bufferStats *stats); static void collectBufferPeriod(bufferStats *stats, bufferStats *fdpStats, callbackTimeStamps *timeStamps, short expectedBufferPeriod); static bool updateBufferStats(bufferStats *stats, int64_t diff_in_nano, int expectedBufferPeriod); static void recordTimeStamp(callbackTimeStamps *timeStamps, int64_t callbackDuration, int64_t timeStamp); int slesComputeDefaultSettings(int /*performanceMode*/, int* /*samplingRate*/, int* /*playerBufferFrameCount*/, int* /*recorderBufferFrameCount*/) { // For OpenSL ES, these parameters can be determined by NativeAudioThread itself. return STATUS_FAIL; } int slesInit(void ** ppCtx, int samplingRate, int frameCount, int micSource, int performanceMode, int testType, double frequency1, char* byteBufferPtr, int byteBufferLength, short* loopbackTone, int maxRecordedLateCallbacks, int ignoreFirstFrames) { sles_data ** ppSles = (sles_data**) ppCtx; int status = STATUS_FAIL; if (ppSles != NULL) { sles_data * pSles = (sles_data*) calloc(1, sizeof(sles_data)); SLES_PRINTF("pSles malloc %zu bytes at %p", sizeof(sles_data), pSles); //__android_log_print(ANDROID_LOG_INFO, "sles_jni", //"malloc %d bytes at %p", sizeof(sles_data), pSles);//Or ANDROID_LOG_INFO, ... *ppSles = pSles; if (pSles != NULL) { SLES_PRINTF("creating server. Sampling rate =%d, frame count = %d", samplingRate, frameCount); status = slesCreateServer(pSles, samplingRate, frameCount, micSource, performanceMode, testType, frequency1, byteBufferPtr, byteBufferLength, loopbackTone, maxRecordedLateCallbacks, ignoreFirstFrames); SLES_PRINTF("slesCreateServer =%d", status); } } return status; } int slesDestroy(void ** ppCtx) { sles_data ** ppSles = (sles_data**)ppCtx; int status = STATUS_FAIL; if (ppSles != NULL) { slesDestroyServer(*ppSles); if (*ppSles != NULL) { SLES_PRINTF("free memory at %p",*ppSles); free(*ppSles); *ppSles = 0; } status = STATUS_SUCCESS; } return status; } #define ASSERT(x) do { if(!(x)) { __android_log_assert("assert", "sles_jni", \ "ASSERTION FAILED: " #x); } } while (0) #define ASSERT_EQ(x, y) do { if ((x) == (y)) ; else __android_log_assert("assert", "sles_jni", \ "ASSERTION FAILED: 0x%x != 0x%x\n", (unsigned) (x), (unsigned) (y)); } while (0) // Called after audio recorder fills a buffer with data, then we can read from this filled buffer static void recorderCallback(SLAndroidSimpleBufferQueueItf caller __unused, void *context) { sles_data *pSles = (sles_data*) context; if (pSles != NULL) { collectBufferPeriod(&pSles->recorderBufferStats, NULL /*fdpStats*/, &pSles->recorderTimeStamps, pSles->expectedBufferPeriod); //__android_log_print(ANDROID_LOG_INFO, "sles_jni", "in recorderCallback"); SLresult result; //ee SLES_PRINTF("rxFront <= pSles->rxBufCount); ASSERT(pSles->rxRear <= pSles->rxBufCount); ASSERT(pSles->rxFront != pSles->rxRear); char *buffer = pSles->rxBuffers[pSles->rxFront]; //pSles->rxBuffers stores the data recorded // Remove buffer from record queue if (++pSles->rxFront > pSles->rxBufCount) { pSles->rxFront = 0; } if (pSles->testType == TEST_TYPE_LATENCY) { // Throw out first frames if (pSles->ignoreFirstFrames) { int framesToErase = pSles->ignoreFirstFrames; if (framesToErase > (int) pSles->bufSizeInFrames) { framesToErase = pSles->bufSizeInFrames; } pSles->ignoreFirstFrames -= framesToErase; memset(buffer, 0, framesToErase * pSles->channels * sizeof(short)); } ssize_t actual = audio_utils_fifo_write(&(pSles->fifo), buffer, (size_t) pSles->bufSizeInFrames); if (actual != (ssize_t) pSles->bufSizeInFrames) { write(1, "?", 1); } // This is called by a realtime (SCHED_FIFO) thread, // and it is unsafe to do I/O as it could block for unbounded time. // Flash filesystem is especially notorious for blocking. if (pSles->fifo2Buffer != NULL) { actual = audio_utils_fifo_write(&(pSles->fifo2), buffer, (size_t) pSles->bufSizeInFrames); if (actual != (ssize_t) pSles->bufSizeInFrames) { write(1, "?", 1); } } } else if (pSles->testType == TEST_TYPE_BUFFER_PERIOD) { if (pSles->fifo2Buffer != NULL) { ssize_t actual = byteBuffer_write(pSles->byteBufferPtr, pSles->byteBufferLength, buffer, (size_t) pSles->bufSizeInFrames, pSles->channels); //FIXME should log errors using other methods instead of printing to terminal if (actual != (ssize_t) pSles->bufSizeInFrames) { write(1, "?", 1); } } } // Enqueue this same buffer for the recorder to fill again. result = (*(pSles->recorderBufferQueue))->Enqueue(pSles->recorderBufferQueue, buffer, pSles->bufSizeInBytes); //__android_log_print(ANDROID_LOG_INFO, "recorderCallback", "recorder buffer size: %i", // pSles->bufSizeInBytes); ASSERT_EQ(SL_RESULT_SUCCESS, result); // Update our model of the record queue SLuint32 rxRearNext = pSles->rxRear + 1; if (rxRearNext > pSles->rxBufCount) { rxRearNext = 0; } ASSERT(rxRearNext != pSles->rxFront); pSles->rxBuffers[pSles->rxRear] = buffer; pSles->rxRear = rxRearNext; //ee SLES_PRINTF("r>"); } //pSles not null } // Calculate nanosecond difference between two timespec structs from clock_gettime(CLOCK_MONOTONIC) // tv_sec [0, max time_t] , tv_nsec [0, 999999999] static int64_t diffInNano(struct timespec previousTime, struct timespec currentTime) { return (int64_t) (currentTime.tv_sec - previousTime.tv_sec) * (int64_t) NANOS_PER_SECOND + currentTime.tv_nsec - previousTime.tv_nsec; } // Called after audio player empties a buffer of data static void playerCallback(SLBufferQueueItf caller __unused, void *context) { sles_data *pSles = (sles_data*) context; if (pSles != NULL) { collectBufferPeriod(&pSles->playerBufferStats, &pSles->recorderBufferStats /*fdpStats*/, &pSles->playerTimeStamps, pSles->expectedBufferPeriod); SLresult result; //ee SLES_PRINTF("txFront <= pSles->txBufCount); ASSERT(pSles->txRear <= pSles->txBufCount); ASSERT(pSles->txFront != pSles->txRear); char *buffer = pSles->txBuffers[pSles->txFront]; if (++pSles->txFront > pSles->txBufCount) { pSles->txFront = 0; } if (pSles->testType == TEST_TYPE_LATENCY) { // Jitter buffer should have strictly less than 2 buffers worth of data in it. // This is to prevent the test itself from adding too much latency. size_t discardedInputFrames = 0; for (;;) { size_t availToRead = audio_utils_fifo_availToRead(&pSles->fifo); if (availToRead < pSles->bufSizeInFrames * 2) { break; } ssize_t actual = audio_utils_fifo_read(&pSles->fifo, buffer, pSles->bufSizeInFrames); if (actual > 0) { discardedInputFrames += actual; } if (actual != (ssize_t) pSles->bufSizeInFrames) { break; } } if (discardedInputFrames > 0) { if (pSles->totalDiscardedInputFrames > 0) { __android_log_print(ANDROID_LOG_WARN, "sles_jni", "Discarded an additional %zu input frames after a total of %zu input frames" " had previously been discarded", discardedInputFrames, pSles->totalDiscardedInputFrames); } pSles->totalDiscardedInputFrames += discardedInputFrames; } ssize_t actual = audio_utils_fifo_read(&(pSles->fifo), buffer, pSles->bufSizeInFrames); if (actual != (ssize_t) pSles->bufSizeInFrames) { write(1, "/", 1); // on underrun from pipe, substitute silence memset(buffer, 0, pSles->bufSizeInFrames * pSles->channels * sizeof(short)); } if (pSles->injectImpulse == -1) { // here we inject pulse /*// Experimentally, a single frame impulse was insufficient to trigger feedback. // Also a Nyquist frequency signal was also insufficient, probably because // the response of output and/or input path was not adequate at high frequencies. // This short burst of a few cycles of square wave at Nyquist/4 found to work well. for (unsigned i = 0; i < pSles->bufSizeInFrames / 8; i += 8) { for (int j = 0; j < 8; j++) { for (unsigned k = 0; k < pSles->channels; k++) { ((short *) buffer)[(i + j) * pSles->channels + k] = j < 4 ? 0x7FFF : 0x8000; } } }*/ //inject java generated tone for (unsigned i = 0; i < pSles->bufSizeInFrames; ++i) { for (unsigned k = 0; k < pSles->channels; ++k) { ((short *) buffer)[i * pSles->channels + k] = pSles->loopbackTone[i]; } } pSles->injectImpulse = 0; pSles->totalDiscardedInputFrames = 0; } } else if (pSles->testType == TEST_TYPE_BUFFER_PERIOD) { double twoPi = M_PI * 2; int maxShort = 32767; float amplitude = 0.8; short value; double phaseIncrement = pSles->frequency1 / pSles->sampleRate; bool isGlitchEnabled = false; for (unsigned i = 0; i < pSles->bufSizeInFrames; i++) { value = (short) (sin(pSles->bufferTestPhase1) * maxShort * amplitude); for (unsigned k = 0; k < pSles->channels; ++k) { ((short *) buffer)[i* pSles->channels + k] = value; } pSles->bufferTestPhase1 += twoPi * phaseIncrement; // insert glitches if isGlitchEnabled == true, and insert it for every second if (isGlitchEnabled && (pSles->count % pSles->sampleRate == 0)) { pSles->bufferTestPhase1 += twoPi * phaseIncrement; } pSles->count++; while (pSles->bufferTestPhase1 > twoPi) { pSles->bufferTestPhase1 -= twoPi; } } } // Enqueue the filled buffer for playing result = (*(pSles->playerBufferQueue))->Enqueue(pSles->playerBufferQueue, buffer, pSles->bufSizeInBytes); ASSERT_EQ(SL_RESULT_SUCCESS, result); // Update our model of the player queue ASSERT(pSles->txFront <= pSles->txBufCount); ASSERT(pSles->txRear <= pSles->txBufCount); SLuint32 txRearNext = pSles->txRear + 1; if (txRearNext > pSles->txBufCount) { txRearNext = 0; } ASSERT(txRearNext != pSles->txFront); pSles->txBuffers[pSles->txRear] = buffer; pSles->txRear = txRearNext; } //pSles not null } // Used to set initial values for the bufferStats struct before values can be recorded. static void initBufferStats(bufferStats *stats) { stats->buffer_period = new int[RANGE](); // initialized to zeros stats->previous_time = {0,0}; stats->current_time = {0,0}; stats->buffer_count = 0; stats->max_buffer_period = 0; stats->measurement_count = 0; stats->SDM = 0; stats->var = 0; } // Called in the beginning of playerCallback() to collect the interval between each callback. // fdpStats is either NULL or a pointer to the buffer statistics for the full-duplex partner. static void collectBufferPeriod(bufferStats *stats, bufferStats *fdpStats, callbackTimeStamps *timeStamps, short expectedBufferPeriod) { clock_gettime(CLOCK_MONOTONIC, &(stats->current_time)); if (timeStamps->startTime.tv_sec == 0 && timeStamps->startTime.tv_nsec == 0) { timeStamps->startTime = stats->current_time; } (stats->buffer_count)++; if (stats->previous_time.tv_sec != 0 && stats->buffer_count > BUFFER_PERIOD_DISCARD && (fdpStats == NULL || fdpStats->buffer_count > BUFFER_PERIOD_DISCARD_FULL_DUPLEX_PARTNER)) { int64_t callbackDuration = diffInNano(stats->previous_time, stats->current_time); bool outlier = updateBufferStats(stats, callbackDuration, expectedBufferPeriod); //recording timestamps of buffer periods not at expected buffer period if (outlier) { int64_t timeStamp = diffInNano(timeStamps->startTime, stats->current_time); recordTimeStamp(timeStamps, callbackDuration, timeStamp); } } stats->previous_time = stats->current_time; } // Records an outlier given the duration in nanoseconds and the number of nanoseconds // between it and the start of the test. static void recordTimeStamp(callbackTimeStamps *timeStamps, int64_t callbackDuration, int64_t timeStamp) { if (timeStamps->exceededCapacity) { return; } //only marked as exceeded if attempting to record a late callback after arrays full if (timeStamps->index == timeStamps->capacity){ timeStamps->exceededCapacity = true; } else { timeStamps->callbackDurations[timeStamps->index] = (short) ((callbackDuration + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI); timeStamps->timeStampsMs[timeStamps->index] = (int) ((timeStamp + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI); timeStamps->index++; } } static void atomicSetIfGreater(volatile int32_t *addr, int32_t val) { // TODO: rewrite this to avoid the need for unbounded spinning int32_t old; do { old = *addr; if (val < old) return; } while(!android_atomic_compare_exchange(&old, val, addr)); } // Updates the stats being collected about buffer periods. Returns true if this is an outlier. static bool updateBufferStats(bufferStats *stats, int64_t diff_in_nano, int expectedBufferPeriod) { stats->measurement_count++; // round up to nearest millisecond int diff_in_milli = (int) ((diff_in_nano + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI); if (diff_in_milli > stats->max_buffer_period) { stats->max_buffer_period = diff_in_milli; } // from 0 ms to 1000 ms, plus a sum of all instances > 1000ms if (diff_in_milli >= (RANGE - 1)) { (stats->buffer_period)[RANGE-1]++; } else if (diff_in_milli >= 0) { (stats->buffer_period)[diff_in_milli]++; } else { // for diff_in_milli < 0 __android_log_print(ANDROID_LOG_INFO, "sles_player", "Having negative BufferPeriod."); } int64_t delta = diff_in_nano - (int64_t) expectedBufferPeriod * NANOS_PER_MILLI; stats->SDM += delta * delta; if (stats->measurement_count > 1) { stats->var = stats->SDM / stats->measurement_count; } // check if the lateness is so bad that a systrace should be captured // TODO: replace static threshold of lateness with a dynamic determination if (diff_in_milli > expectedBufferPeriod + LATE_CALLBACK_CAPTURE_THRESHOLD) { // TODO: log in a non-blocking way //__android_log_print(ANDROID_LOG_INFO, "buffer_stats", "Callback late by %d ms", // diff_in_milli - expectedBufferPeriod); atomicSetIfGreater(&(stats->captureRank), diff_in_milli - expectedBufferPeriod); } return diff_in_milli > expectedBufferPeriod + LATE_CALLBACK_OUTLIER_THRESHOLD; } static int slesCreateServer(sles_data *pSles, int samplingRate, int frameCount, int micSource, int performanceMode, int testType, double frequency1, char *byteBufferPtr, int byteBufferLength, short *loopbackTone, int maxRecordedLateCallbacks, int ignoreFirstFrames) { int status = STATUS_FAIL; if (pSles != NULL) { // adb shell slesTest_feedback -r1 -t1 -s48000 -f240 -i300 -e3 -o/sdcard/log.wav // r1 and t1 are the receive and transmit buffer counts, typically 1 // s is the sample rate, typically 48000 or 44100 // f is the frame count per buffer, typically 240 or 256 // i is the number of milliseconds before impulse. You may need to adjust this. // e is number of seconds to record // o is output .wav file name // // default values // SLuint32 rxBufCount = 1; // -r# // SLuint32 txBufCount = 1; // -t# // SLuint32 bufSizeInFrames = 240; // -f# // SLuint32 channels = 1; // -c# // SLuint32 sampleRate = 48000; // -s# // SLuint32 exitAfterSeconds = 3; // -e# // SLuint32 freeBufCount = 0; // calculated // SLuint32 bufSizeInBytes = 0; // calculated // int injectImpulse = 300; // -i#i // // // Storage area for the buffer queues // char **rxBuffers; // char **txBuffers; // char **freeBuffers; // // // Buffer indices // SLuint32 rxFront; // oldest recording // SLuint32 rxRear; // next to be recorded // SLuint32 txFront; // oldest playing // SLuint32 txRear; // next to be played // SLuint32 freeFront; // oldest free // SLuint32 freeRear; // next to be freed // // audio_utils_fifo fifo; //(*) // SLAndroidSimpleBufferQueueItf recorderBufferQueue; // SLBufferQueueItf playerBufferQueue; // default values pSles->rxBufCount = 1; // -r# pSles->txBufCount = 1; // -t# pSles->bufSizeInFrames = frameCount;//240; // -f# pSles->channels = 1; // -c# pSles->sampleRate = samplingRate;//48000; // -s# pSles->exitAfterSeconds = 3; // -e# pSles->freeBufCount = 0; // calculated pSles->bufSizeInBytes = 0; // calculated pSles->injectImpulse = 300; // -i#i pSles->totalDiscardedInputFrames = 0; pSles->ignoreFirstFrames = ignoreFirstFrames; // Storage area for the buffer queues // char **rxBuffers; // char **txBuffers; // char **freeBuffers; // Buffer indices #if 0 pSles->rxFront; // oldest recording pSles->rxRear; // next to be recorded pSles->txFront; // oldest playing pSles->txRear; // next to be played pSles->freeFront; // oldest free pSles->freeRear; // next to be freed pSles->fifo; //(*) #endif pSles->fifo2Buffer = NULL; //this fifo is for sending data to java code (to plot it) #if 0 pSles->recorderBufferQueue; pSles->playerBufferQueue; #endif // compute total free buffers as -r plus -t pSles->freeBufCount = pSles->rxBufCount + pSles->txBufCount; // compute buffer size pSles->bufSizeInBytes = pSles->channels * pSles->bufSizeInFrames * sizeof(short); // Initialize free buffers pSles->freeBuffers = (char **) calloc(pSles->freeBufCount + 1, sizeof(char *)); SLES_PRINTF(" calloc freeBuffers %llu bytes at %p", (long long)pSles->freeBufCount + 1, pSles->freeBuffers); unsigned j; for (j = 0; j < pSles->freeBufCount; ++j) { pSles->freeBuffers[j] = (char *) malloc(pSles->bufSizeInBytes); SLES_PRINTF(" buff%d malloc %llu bytes at %p",j, (long long)pSles->bufSizeInBytes, pSles->freeBuffers[j]); } pSles->freeFront = 0; pSles->freeRear = pSles->freeBufCount; pSles->freeBuffers[j] = NULL; // Initialize record queue pSles->rxBuffers = (char **) calloc(pSles->rxBufCount + 1, sizeof(char *)); SLES_PRINTF(" calloc rxBuffers %llu bytes at %p", (long long)pSles->rxBufCount + 1, pSles->rxBuffers); pSles->rxFront = 0; pSles->rxRear = 0; // Initialize play queue pSles->txBuffers = (char **) calloc(pSles->txBufCount + 1, sizeof(char *)); SLES_PRINTF(" calloc txBuffers %llu bytes at %p", (long long)pSles->txBufCount + 1, pSles->txBuffers); pSles->txFront = 0; pSles->txRear = 0; size_t frameSize = pSles->channels * sizeof(short); #define FIFO_FRAMES 1024 pSles->fifoBuffer = new short[FIFO_FRAMES * pSles->channels]; audio_utils_fifo_init(&(pSles->fifo), FIFO_FRAMES, frameSize, pSles->fifoBuffer); // SNDFILE *sndfile; // if (outFileName != NULL) { // create .wav writer // SF_INFO info; // info.frames = 0; // info.samplerate = sampleRate; // info.channels = channels; // info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; // sndfile = sf_open(outFileName, SFM_WRITE, &info); // if (sndfile != NULL) { #define FIFO2_FRAMES 65536 pSles->fifo2Buffer = new short[FIFO2_FRAMES * pSles->channels]; audio_utils_fifo_init(&(pSles->fifo2), FIFO2_FRAMES, frameSize, pSles->fifo2Buffer); // } else { // fprintf(stderr, "sf_open failed\n"); // } // } else { // sndfile = NULL; // } initBufferStats(&pSles->recorderBufferStats); initBufferStats(&pSles->playerBufferStats); // init other variables needed for buffer test pSles->testType = testType; pSles->frequency1 = frequency1; pSles->bufferTestPhase1 = 0; pSles->count = 0; pSles->byteBufferPtr = byteBufferPtr; pSles->byteBufferLength = byteBufferLength; //init loopback tone pSles->loopbackTone = loopbackTone; pSles->recorderTimeStamps = { new int[maxRecordedLateCallbacks], //int* timeStampsMs new short[maxRecordedLateCallbacks], //short* callbackDurations 0, //short index {0,0}, //struct timespec startTime; maxRecordedLateCallbacks, //int capacity false //bool exceededCapacity }; pSles->playerTimeStamps = { new int[maxRecordedLateCallbacks], //int* timeStampsMs new short[maxRecordedLateCallbacks], //short* callbackDurations; 0, //short index {0,0}, //struct timespec startTime; maxRecordedLateCallbacks, //int capacity false //bool exceededCapacity }; pSles->expectedBufferPeriod = (short) ( round(pSles->bufSizeInFrames * MILLIS_PER_SECOND / (float) pSles->sampleRate)); SLresult result; // create engine #if 0 pSles->engineObject; #endif result = slCreateEngine(&(pSles->engineObject), 0, NULL, 0, NULL, NULL); ASSERT_EQ(SL_RESULT_SUCCESS, result); result = (*(pSles->engineObject))->Realize(pSles->engineObject, SL_BOOLEAN_FALSE); ASSERT_EQ(SL_RESULT_SUCCESS, result); SLEngineItf engineEngine; result = (*(pSles->engineObject))->GetInterface(pSles->engineObject, SL_IID_ENGINE, &engineEngine); ASSERT_EQ(SL_RESULT_SUCCESS, result); // create output mix #if 0 pSles->outputmixObject; #endif result = (*engineEngine)->CreateOutputMix(engineEngine, &(pSles->outputmixObject), 0, NULL, NULL); ASSERT_EQ(SL_RESULT_SUCCESS, result); result = (*(pSles->outputmixObject))->Realize(pSles->outputmixObject, SL_BOOLEAN_FALSE); ASSERT_EQ(SL_RESULT_SUCCESS, result); // create an audio player with buffer queue source and output mix sink SLDataSource audiosrc; SLDataSink audiosnk; SLDataFormat_PCM pcm; SLDataLocator_OutputMix locator_outputmix; SLDataLocator_BufferQueue locator_bufferqueue_tx; locator_bufferqueue_tx.locatorType = SL_DATALOCATOR_BUFFERQUEUE; locator_bufferqueue_tx.numBuffers = pSles->txBufCount; locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; locator_outputmix.outputMix = pSles->outputmixObject; pcm.formatType = SL_DATAFORMAT_PCM; pcm.numChannels = pSles->channels; pcm.samplesPerSec = pSles->sampleRate * 1000; pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; pcm.containerSize = 16; pcm.channelMask = pSles->channels == 1 ? SL_SPEAKER_FRONT_CENTER : (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; audiosrc.pLocator = &locator_bufferqueue_tx; audiosrc.pFormat = &pcm; audiosnk.pLocator = &locator_outputmix; audiosnk.pFormat = NULL; pSles->playerObject = NULL; pSles->recorderObject = NULL; SLInterfaceID ids_tx[2] = {SL_IID_BUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION}; SLboolean flags_tx[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; result = (*engineEngine)->CreateAudioPlayer(engineEngine, &(pSles->playerObject), &audiosrc, &audiosnk, 2, ids_tx, flags_tx); if (SL_RESULT_CONTENT_UNSUPPORTED == result) { SLES_PRINTF("ERROR: Could not create audio player (result %x), check sample rate\n", result); goto cleanup; } ASSERT_EQ(SL_RESULT_SUCCESS, result); { /* Get the Android configuration interface which is explicit */ SLAndroidConfigurationItf configItf; result = (*(pSles->playerObject))->GetInterface(pSles->playerObject, SL_IID_ANDROIDCONFIGURATION, (void*)&configItf); ASSERT_EQ(SL_RESULT_SUCCESS, result); /* Use the configuration interface to configure the player before it's realized */ if (performanceMode != -1) { SLuint32 performanceMode32 = performanceMode; result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode32, sizeof(SLuint32)); ASSERT_EQ(SL_RESULT_SUCCESS, result); } } result = (*(pSles->playerObject))->Realize(pSles->playerObject, SL_BOOLEAN_FALSE); ASSERT_EQ(SL_RESULT_SUCCESS, result); SLPlayItf playerPlay; result = (*(pSles->playerObject))->GetInterface(pSles->playerObject, SL_IID_PLAY, &playerPlay); ASSERT_EQ(SL_RESULT_SUCCESS, result); result = (*(pSles->playerObject))->GetInterface(pSles->playerObject, SL_IID_BUFFERQUEUE, &(pSles->playerBufferQueue)); ASSERT_EQ(SL_RESULT_SUCCESS, result); result = (*(pSles->playerBufferQueue))->RegisterCallback(pSles->playerBufferQueue, playerCallback, pSles); //playerCallback is the name of callback function ASSERT_EQ(SL_RESULT_SUCCESS, result); // Enqueue some zero buffers for the player for (j = 0; j < pSles->txBufCount; ++j) { // allocate a free buffer ASSERT(pSles->freeFront != pSles->freeRear); char *buffer = pSles->freeBuffers[pSles->freeFront]; if (++pSles->freeFront > pSles->freeBufCount) { pSles->freeFront = 0; } // put on play queue SLuint32 txRearNext = pSles->txRear + 1; if (txRearNext > pSles->txBufCount) { txRearNext = 0; } ASSERT(txRearNext != pSles->txFront); pSles->txBuffers[pSles->txRear] = buffer; pSles->txRear = txRearNext; result = (*(pSles->playerBufferQueue))->Enqueue(pSles->playerBufferQueue, buffer, pSles->bufSizeInBytes); ASSERT_EQ(SL_RESULT_SUCCESS, result); } result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING); ASSERT_EQ(SL_RESULT_SUCCESS, result); // Create an audio recorder with microphone device source and buffer queue sink. // The buffer queue as sink is an Android-specific extension. SLDataLocator_IODevice locator_iodevice; SLDataLocator_AndroidSimpleBufferQueue locator_bufferqueue_rx; locator_iodevice.locatorType = SL_DATALOCATOR_IODEVICE; locator_iodevice.deviceType = SL_IODEVICE_AUDIOINPUT; locator_iodevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; locator_iodevice.device = NULL; audiosrc.pLocator = &locator_iodevice; audiosrc.pFormat = NULL; locator_bufferqueue_rx.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; locator_bufferqueue_rx.numBuffers = pSles->rxBufCount; audiosnk.pLocator = &locator_bufferqueue_rx; audiosnk.pFormat = &pcm; { //why brackets here? SLInterfaceID ids_rx[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION}; SLboolean flags_rx[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; result = (*engineEngine)->CreateAudioRecorder(engineEngine, &(pSles->recorderObject), &audiosrc, &audiosnk, 2, ids_rx, flags_rx); if (SL_RESULT_SUCCESS != result) { status = STATUS_FAIL; SLES_PRINTF("ERROR: Could not create audio recorder (result %x), " "check sample rate and channel count\n", result); goto cleanup; } } ASSERT_EQ(SL_RESULT_SUCCESS, result); { /* Get the Android configuration interface which is explicit */ SLAndroidConfigurationItf configItf; result = (*(pSles->recorderObject))->GetInterface(pSles->recorderObject, SL_IID_ANDROIDCONFIGURATION, (void*)&configItf); ASSERT_EQ(SL_RESULT_SUCCESS, result); SLuint32 presetValue = micSource; //SL_ANDROID_RECORDING_PRESET_CAMCORDER;//SL_ANDROID_RECORDING_PRESET_NONE; /* Use the configuration interface to configure the recorder before it's realized */ if (presetValue != SL_ANDROID_RECORDING_PRESET_NONE) { result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET, &presetValue, sizeof(SLuint32)); ASSERT_EQ(SL_RESULT_SUCCESS, result); } if (performanceMode != -1) { SLuint32 performanceMode32 = performanceMode; result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode32, sizeof(SLuint32)); ASSERT_EQ(SL_RESULT_SUCCESS, result); } } result = (*(pSles->recorderObject))->Realize(pSles->recorderObject, SL_BOOLEAN_FALSE); ASSERT_EQ(SL_RESULT_SUCCESS, result); SLRecordItf recorderRecord; result = (*(pSles->recorderObject))->GetInterface(pSles->recorderObject, SL_IID_RECORD, &recorderRecord); ASSERT_EQ(SL_RESULT_SUCCESS, result); result = (*(pSles->recorderObject))->GetInterface(pSles->recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(pSles->recorderBufferQueue)); ASSERT_EQ(SL_RESULT_SUCCESS, result); result = (*(pSles->recorderBufferQueue))->RegisterCallback(pSles->recorderBufferQueue, recorderCallback, pSles); ASSERT_EQ(SL_RESULT_SUCCESS, result); // Enqueue some empty buffers for the recorder for (j = 0; j < pSles->rxBufCount; ++j) { // allocate a free buffer ASSERT(pSles->freeFront != pSles->freeRear); char *buffer = pSles->freeBuffers[pSles->freeFront]; if (++pSles->freeFront > pSles->freeBufCount) { pSles->freeFront = 0; } // put on record queue SLuint32 rxRearNext = pSles->rxRear + 1; if (rxRearNext > pSles->rxBufCount) { rxRearNext = 0; } ASSERT(rxRearNext != pSles->rxFront); pSles->rxBuffers[pSles->rxRear] = buffer; pSles->rxRear = rxRearNext; result = (*(pSles->recorderBufferQueue))->Enqueue(pSles->recorderBufferQueue, buffer, pSles->bufSizeInBytes); ASSERT_EQ(SL_RESULT_SUCCESS, result); } // Kick off the recorder result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING); ASSERT_EQ(SL_RESULT_SUCCESS, result); // Tear down the objects and exit status = STATUS_SUCCESS; cleanup: SLES_PRINTF("Finished initialization with status: %d", status); } return status; } // Read data from fifo2Buffer and store into pSamples. int slesProcessNext(void *pCtx, double *pSamples, long maxSamples) { //int status = STATUS_FAIL; sles_data *pSles = (sles_data*)pCtx; SLES_PRINTF("slesProcessNext: pSles = %p, currentSample: %p, maxSamples = %ld", pSles, pSamples, maxSamples); int samplesRead = 0; int currentSample = 0; double *pCurrentSample = pSamples; int maxValue = 32768; if (pSles != NULL) { SLresult result; for (int i = 0; i < 10; i++) { usleep(100000); // sleep for 0.1s if (pSles->fifo2Buffer != NULL) { for (;;) { short buffer[pSles->bufSizeInFrames * pSles->channels]; ssize_t actual = audio_utils_fifo_read(&(pSles->fifo2), buffer, pSles->bufSizeInFrames); if (actual <= 0) break; { for (int jj = 0; jj < actual && currentSample < maxSamples; jj++) { *(pCurrentSample++) = ((double) buffer[jj]) / maxValue; currentSample++; } } samplesRead += actual; } } if (pSles->injectImpulse > 0) { if (pSles->injectImpulse <= 100) { pSles->injectImpulse = -1; write(1, "I", 1); } else { if ((pSles->injectImpulse % 1000) < 100) { write(1, "i", 1); } pSles->injectImpulse -= 100; } } else if (i == 9) { write(1, ".", 1); } } SLBufferQueueState playerBQState; result = (*(pSles->playerBufferQueue))->GetState(pSles->playerBufferQueue, &playerBQState); ASSERT_EQ(SL_RESULT_SUCCESS, result); SLAndroidSimpleBufferQueueState recorderBQState; result = (*(pSles->recorderBufferQueue))->GetState(pSles->recorderBufferQueue, &recorderBQState); ASSERT_EQ(SL_RESULT_SUCCESS, result); SLES_PRINTF("End of slesProcessNext: pSles = %p, samplesRead = %d, maxSamples = %ld", pSles, samplesRead, maxSamples); } return samplesRead; } static int slesDestroyServer(sles_data *pSles) { int status = STATUS_FAIL; SLES_PRINTF("Start slesDestroyServer: pSles = %p", pSles); if (pSles != NULL) { if (NULL != pSles->playerObject) { SLES_PRINTF("stopping player..."); SLPlayItf playerPlay; SLresult result = (*(pSles->playerObject))->GetInterface(pSles->playerObject, SL_IID_PLAY, &playerPlay); ASSERT_EQ(SL_RESULT_SUCCESS, result); //stop player and recorder if they exist result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_STOPPED); ASSERT_EQ(SL_RESULT_SUCCESS, result); } if (NULL != pSles->recorderObject) { SLES_PRINTF("stopping recorder..."); SLRecordItf recorderRecord; SLresult result = (*(pSles->recorderObject))->GetInterface(pSles->recorderObject, SL_IID_RECORD, &recorderRecord); ASSERT_EQ(SL_RESULT_SUCCESS, result); result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED); ASSERT_EQ(SL_RESULT_SUCCESS, result); } usleep(1000); audio_utils_fifo_deinit(&(pSles->fifo)); delete[] pSles->fifoBuffer; SLES_PRINTF("slesDestroyServer 2"); // if (sndfile != NULL) { audio_utils_fifo_deinit(&(pSles->fifo2)); delete[] pSles->fifo2Buffer; SLES_PRINTF("slesDestroyServer 3"); // sf_close(sndfile); // } if (NULL != pSles->playerObject) { (*(pSles->playerObject))->Destroy(pSles->playerObject); } SLES_PRINTF("slesDestroyServer 4"); if (NULL != pSles->recorderObject) { (*(pSles->recorderObject))->Destroy(pSles->recorderObject); } SLES_PRINTF("slesDestroyServer 5"); (*(pSles->outputmixObject))->Destroy(pSles->outputmixObject); SLES_PRINTF("slesDestroyServer 6"); (*(pSles->engineObject))->Destroy(pSles->engineObject); SLES_PRINTF("slesDestroyServer 7"); //free buffers if (NULL != pSles->freeBuffers) { for (unsigned j = 0; j < pSles->freeBufCount; ++j) { if (NULL != pSles->freeBuffers[j]) { SLES_PRINTF(" free buff%d at %p",j, pSles->freeBuffers[j]); free (pSles->freeBuffers[j]); } } SLES_PRINTF(" free freeBuffers at %p", pSles->freeBuffers); free(pSles->freeBuffers); } else { SLES_PRINTF(" freeBuffers NULL, no need to free"); } if (NULL != pSles->rxBuffers) { SLES_PRINTF(" free rxBuffers at %p", pSles->rxBuffers); free(pSles->rxBuffers); } else { SLES_PRINTF(" rxBuffers NULL, no need to free"); } if (NULL != pSles->txBuffers) { SLES_PRINTF(" free txBuffers at %p", pSles->txBuffers); free(pSles->txBuffers); } else { SLES_PRINTF(" txBuffers NULL, no need to free"); } status = STATUS_SUCCESS; } SLES_PRINTF("End slesDestroyServer: status = %d", status); return status; } int* slesGetRecorderBufferPeriod(void *pCtx) { sles_data *pSles = (sles_data*)pCtx; return pSles->recorderBufferStats.buffer_period; } int slesGetRecorderMaxBufferPeriod(void *pCtx) { sles_data *pSles = (sles_data*)pCtx; return pSles->recorderBufferStats.max_buffer_period; } int64_t slesGetRecorderVarianceBufferPeriod(void *pCtx) { sles_data *pSles = (sles_data*)pCtx; return pSles->recorderBufferStats.var; } int* slesGetPlayerBufferPeriod(void *pCtx) { sles_data *pSles = (sles_data*)pCtx; return pSles->playerBufferStats.buffer_period; } int slesGetPlayerMaxBufferPeriod(void *pCtx) { sles_data *pSles = (sles_data*)pCtx; return pSles->playerBufferStats.max_buffer_period; } int64_t slesGetPlayerVarianceBufferPeriod(void *pCtx) { sles_data *pSles = (sles_data*)pCtx; return pSles->playerBufferStats.var; } int slesGetCaptureRank(void *pCtx) { sles_data *pSles = (sles_data*)pCtx; // clear the capture flags since they're being handled now int recorderRank = android_atomic_exchange(0, &pSles->recorderBufferStats.captureRank); int playerRank = android_atomic_exchange(0, &pSles->playerBufferStats.captureRank); if (recorderRank > playerRank) { return recorderRank; } else { return playerRank; } } int slesGetPlayerTimeStampsAndExpectedBufferPeriod(void *pCtx, callbackTimeStamps **ppTSs) { sles_data *pSles = (sles_data*)pCtx; *ppTSs = &pSles->playerTimeStamps; return pSles->expectedBufferPeriod; } int slesGetRecorderTimeStampsAndExpectedBufferPeriod(void *pCtx, callbackTimeStamps **ppTSs) { sles_data *pSles = (sles_data*)pCtx; *ppTSs = &pSles->recorderTimeStamps; return pSles->expectedBufferPeriod; }