diff options
Diffstat (limited to 'LoopbackApp/app/src/main/jni/sles.cpp')
-rw-r--r-- | LoopbackApp/app/src/main/jni/sles.cpp | 1051 |
1 files changed, 1051 insertions, 0 deletions
diff --git a/LoopbackApp/app/src/main/jni/sles.cpp b/LoopbackApp/app/src/main/jni/sles.cpp new file mode 100644 index 0000000..159269b --- /dev/null +++ b/LoopbackApp/app/src/main/jni/sles.cpp @@ -0,0 +1,1051 @@ +/* + * 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 <cmath> +#include "sles.h" +#include "audio_utils/atomic.h" +#include <stdio.h> +#include <assert.h> +#include <unistd.h> +#include <string.h> + +int slesInit(sles_data ** ppSles, int samplingRate, int frameCount, int micSource, + int performanceMode, + int testType, double frequency1, char* byteBufferPtr, int byteBufferLength, + short* loopbackTone, int maxRecordedLateCallbacks, int ignoreFirstFrames) { + int status = SLES_FAIL; + if (ppSles != NULL) { + sles_data * pSles = (sles_data*) malloc(sizeof(sles_data)); + + memset(pSles, 0, 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(sles_data ** ppSles) { + int status = SLES_FAIL; + if (ppSles != NULL) { + slesDestroyServer(*ppSles); + + if (*ppSles != NULL) + { + SLES_PRINTF("free memory at %p",*ppSles); + free(*ppSles); + *ppSles = 0; + } + status = SLES_SUCCESS; + } + return status; +} + +#define ASSERT_EQ(x, y) do { if ((x) == (y)) ; else { fprintf(stderr, "0x%x != 0x%x\n", \ + (unsigned) (x), (unsigned) (y)); assert((x) == (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("<R"); + + // We should only be called when a recording buffer is done + assert(pSles->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, buffer, (size_t) pSles->bufSizeInFrames); + + //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 +} + + +// Write "count" amount of short from buffer to pSles->byteBufferPtr. This byteBuffer will read by +// java code. +ssize_t byteBuffer_write(sles_data *pSles, char *buffer, size_t count) { + // bytebufferSize is in byte + int32_t rear; // rear should not exceed 2^31 - 1, or else overflow will happen + memcpy(&rear, (char *) (pSles->byteBufferPtr + pSles->byteBufferLength - 4), sizeof(rear)); + + size_t frameSize = pSles->channels * sizeof(short); // only one channel + int32_t maxLengthInShort = (pSles->byteBufferLength - 4) / frameSize; + // mask the upper bits to get the correct position in the pipe + int32_t tempRear = rear & (maxLengthInShort - 1); + size_t part1 = maxLengthInShort - tempRear; + + if (part1 > count) { + part1 = count; + } + + if (part1 > 0) { + memcpy(pSles->byteBufferPtr + (tempRear * frameSize), buffer, + part1 * frameSize); + + size_t part2 = count - part1; + if (part2 > 0) { + memcpy(pSles->byteBufferPtr, (buffer + (part1 * frameSize)), + part2 * frameSize); + } + + //TODO do we need something similar to the below function call? + //android_atomic_release_store(audio_utils_fifo_sum(fifo, fifo->mRear, availToWrite), + // &fifo->mRear); + } + + // increase value of rear + int32_t* rear2 = (int32_t *) (pSles->byteBufferPtr + pSles->byteBufferLength - 4); + *rear2 += count; + return count; +} + +// Calculate nanosecond difference between two timespec structs from clock_gettime(CLOCK_MONOTONIC) +// tv_sec [0, max time_t] , tv_nsec [0, 999999999] +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("<P"); + + // Get the buffer that just finished playing + assert(pSles->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. +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. +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. +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++; + } +} + +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. +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; +} + +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 = SLES_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 + 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; //(*) + pSles->fifo2Buffer = NULL; //this fifo is for sending data to java code (to plot it) + pSles->recorderBufferQueue; + pSles->playerBufferQueue; + + + + // 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 %zu bytes at %p",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 %zu bytes at %p",j, 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 %zu bytes at %p",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 %zu bytes at %p",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 + pSles->engineObject; + 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 + pSles->outputmixObject; + 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) { + fprintf(stderr, "Could not create audio player (result %x), check sample rate\n", + 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) { + fprintf(stderr, "Could not create audio recorder (result %x), " + "check sample rate and channel count\n", result); + status = SLES_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 = SLES_SUCCESS; + cleanup: + + SLES_PRINTF("Finished initialization with status: %d", status); + + int xx = 1; + + } + return status; +} + +// Read data from fifo2Buffer and store into pSamples. +int slesProcessNext(sles_data *pSles, double *pSamples, long maxSamples) { + //int status = SLES_FAIL; + + 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; +} + + +int slesDestroyServer(sles_data *pSles) { + int status = SLES_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 = SLES_SUCCESS; + } + SLES_PRINTF("End slesDestroyServer: status = %d", status); + return status; +} + + +int* slesGetRecorderBufferPeriod(sles_data *pSles) { + return pSles->recorderBufferStats.buffer_period; +} + +int slesGetRecorderMaxBufferPeriod(sles_data *pSles) { + return pSles->recorderBufferStats.max_buffer_period; +} + +int64_t slesGetRecorderVarianceBufferPeriod(sles_data *pSles) { + return pSles->recorderBufferStats.var; +} + +int* slesGetPlayerBufferPeriod(sles_data *pSles) { + return pSles->playerBufferStats.buffer_period; +} + +int slesGetPlayerMaxBufferPeriod(sles_data *pSles) { + return pSles->playerBufferStats.max_buffer_period; +} + +int64_t slesGetPlayerVarianceBufferPeriod(sles_data *pSles) { + return pSles->playerBufferStats.var; +} + +int slesGetCaptureRank(sles_data *pSles) { + // 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; + } +} |