diff options
Diffstat (limited to 'LoopbackApp/app/src/main/jni')
-rw-r--r-- | LoopbackApp/app/src/main/jni/Android.mk | 27 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/jni/audio_utils/atomic.c | 40 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/jni/audio_utils/atomic.h | 38 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/jni/audio_utils/fifo.c | 154 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/jni/audio_utils/fifo.h | 88 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/jni/audio_utils/roundup.c | 32 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/jni/audio_utils/roundup.h | 31 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/jni/jni_sles.c | 178 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/jni/jni_sles.h | 69 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/jni/sles.cpp | 1051 | ||||
-rw-r--r-- | LoopbackApp/app/src/main/jni/sles.h | 174 |
11 files changed, 0 insertions, 1882 deletions
diff --git a/LoopbackApp/app/src/main/jni/Android.mk b/LoopbackApp/app/src/main/jni/Android.mk deleted file mode 100644 index ef0c829..0000000 --- a/LoopbackApp/app/src/main/jni/Android.mk +++ /dev/null @@ -1,27 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := libloopback -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := \ - sles.cpp \ - jni_sles.c \ - audio_utils/atomic.c \ - audio_utils/fifo.c \ - audio_utils/roundup.c -LOCAL_C_INCLUDES := \ - frameworks/wilhelm/include - -LOCAL_SHARED_LIBRARIES := \ - libOpenSLES \ - liblog \ - libandroid - -LOCAL_LDLIBS += -lOpenSLES -llog -landroid -#LOCAL_PRELINK_MODULE := false - -#LOCAL_LDFLAGS += -Wl,--hash-style=sysv -#LOCAL_CFLAGS := -DSTDC_HEADERS - -include $(BUILD_SHARED_LIBRARY) diff --git a/LoopbackApp/app/src/main/jni/audio_utils/atomic.c b/LoopbackApp/app/src/main/jni/audio_utils/atomic.c deleted file mode 100644 index b76b1f4..0000000 --- a/LoopbackApp/app/src/main/jni/audio_utils/atomic.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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. - */ - -#include "atomic.h" - -#include <stdatomic.h> -#include <stdbool.h> - -int32_t android_atomic_acquire_load(volatile const int32_t* addr) { - volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*) addr; - return atomic_load_explicit(a, memory_order_acquire); -} - -void android_atomic_release_store(int32_t value, volatile int32_t* addr) { - volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*) addr; - atomic_store_explicit(a, value, memory_order_release); -} - -int32_t android_atomic_exchange(int32_t value, volatile const int32_t* addr) { - volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*) addr; - return atomic_exchange(a, value); -} - -bool android_atomic_compare_exchange(int32_t* expect, int32_t desire, volatile const int32_t* addr) { - volatile atomic_int_least32_t* a = (volatile atomic_int_least32_t*) addr; - return atomic_compare_exchange_weak(a, expect, desire); -} diff --git a/LoopbackApp/app/src/main/jni/audio_utils/atomic.h b/LoopbackApp/app/src/main/jni/audio_utils/atomic.h deleted file mode 100644 index 164ad17..0000000 --- a/LoopbackApp/app/src/main/jni/audio_utils/atomic.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_AUDIO_ATOMIC_H -#define ANDROID_AUDIO_ATOMIC_H - -#include <stdlib.h> -#include <stdbool.h> - -#ifdef __cplusplus -extern "C" { -#endif - -int32_t android_atomic_acquire_load(volatile const int32_t* addr); -void android_atomic_release_store(int32_t value, volatile int32_t* addr); - -// FIXME: use standard atomic library instead of these functions -int32_t android_atomic_exchange(int32_t value, volatile const int32_t* addr); -bool android_atomic_compare_exchange(int32_t* expect, int32_t desire, volatile const int32_t* addr); - -#ifdef __cplusplus -} -#endif - -#endif // ANDROID_AUDIO_ATOMIC_H diff --git a/LoopbackApp/app/src/main/jni/audio_utils/fifo.c b/LoopbackApp/app/src/main/jni/audio_utils/fifo.c deleted file mode 100644 index e00fc28..0000000 --- a/LoopbackApp/app/src/main/jni/audio_utils/fifo.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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. - */ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "audio_utils_fifo" - -#include <stdlib.h> -#include <string.h> -#include "fifo.h" -#include "roundup.h" -#include "atomic.h" -//#include <cutils/log.h> -#define ALOG_ASSERT(exp) - - -void audio_utils_fifo_init(struct audio_utils_fifo *fifo, size_t frameCount, size_t frameSize, - void *buffer) { - // We would need a 64-bit roundup to support larger frameCount. - ALOG_ASSERT(fifo != NULL && frameCount > 0 && frameSize > 0 && buffer != NULL); - fifo->mFrameCount = frameCount; - fifo->mFrameCountP2 = roundup(frameCount); - fifo->mFudgeFactor = fifo->mFrameCountP2 - fifo->mFrameCount; - fifo->mFrameSize = frameSize; - fifo->mBuffer = buffer; - fifo->mFront = 0; - fifo->mRear = 0; -} - - -void audio_utils_fifo_deinit(struct audio_utils_fifo *fifo __unused) -{ -} - - -// Return a new index as the sum of an old index (either mFront or mRear) and a specified increment. -static inline int32_t audio_utils_fifo_sum(struct audio_utils_fifo *fifo, int32_t index, - uint32_t increment) { - if (fifo->mFudgeFactor) { - uint32_t mask = fifo->mFrameCountP2 - 1; - ALOG_ASSERT((index & mask) < fifo->mFrameCount); - ALOG_ASSERT(/*0 <= increment &&*/ increment <= fifo->mFrameCountP2); - if ((index & mask) + increment >= fifo->mFrameCount) { - increment += fifo->mFudgeFactor; - } - - index += increment; - ALOG_ASSERT((index & mask) < fifo->mFrameCount); - return index; - } else { - return index + increment; - } -} - - -// Return the difference between two indices: rear - front, where 0 <= difference <= mFrameCount. -static inline size_t audio_utils_fifo_diff(struct audio_utils_fifo *fifo, int32_t rear, - int32_t front) { - int32_t diff = rear - front; - - if (fifo->mFudgeFactor) { - uint32_t mask = ~(fifo->mFrameCountP2 - 1); - int32_t genDiff = (rear & mask) - (front & mask); - - if (genDiff != 0) { - ALOG_ASSERT(genDiff == (int32_t) fifo->mFrameCountP2); - diff -= fifo->mFudgeFactor; - } - } - - // FIFO should not be overfull - ALOG_ASSERT(0 <= diff && diff <= (int32_t) fifo->mFrameCount); - return (size_t) diff; -} - - -ssize_t audio_utils_fifo_write(struct audio_utils_fifo *fifo, const void *buffer, size_t count) { - int32_t front = android_atomic_acquire_load(&fifo->mFront); - int32_t rear = fifo->mRear; - size_t availToWrite = fifo->mFrameCount - audio_utils_fifo_diff(fifo, rear, front); - - if (availToWrite > count) { - availToWrite = count; - } - - rear &= fifo->mFrameCountP2 - 1; - size_t part1 = fifo->mFrameCount - rear; - if (part1 > availToWrite) { - part1 = availToWrite; - } - - if (part1 > 0) { - memcpy((char *) fifo->mBuffer + (rear * fifo->mFrameSize), buffer, - part1 * fifo->mFrameSize); - size_t part2 = availToWrite - part1; - - if (part2 > 0) { - memcpy(fifo->mBuffer, (char *) buffer + (part1 * fifo->mFrameSize), - part2 * fifo->mFrameSize); - } - - android_atomic_release_store(audio_utils_fifo_sum(fifo, fifo->mRear, availToWrite), - &fifo->mRear); - } - return availToWrite; -} - - -ssize_t audio_utils_fifo_read(struct audio_utils_fifo *fifo, void *buffer, size_t count) { - int32_t rear = android_atomic_acquire_load(&fifo->mRear); - int32_t front = fifo->mFront; - size_t availToRead = audio_utils_fifo_diff(fifo, rear, front); - if (availToRead > count) { - availToRead = count; - } - - front &= fifo->mFrameCountP2 - 1; - size_t part1 = fifo->mFrameCount - front; - if (part1 > availToRead) { - part1 = availToRead; - } - - if (part1 > 0) { - memcpy(buffer, (char *) fifo->mBuffer + (front * fifo->mFrameSize), - part1 * fifo->mFrameSize); - size_t part2 = availToRead - part1; - if (part2 > 0) { - memcpy((char *) buffer + (part1 * fifo->mFrameSize), fifo->mBuffer, - part2 * fifo->mFrameSize); - } - android_atomic_release_store(audio_utils_fifo_sum(fifo, fifo->mFront, availToRead), - &fifo->mFront); - } - return availToRead; -} - -size_t audio_utils_fifo_availToRead(struct audio_utils_fifo *fifo) { - int32_t rear = android_atomic_acquire_load(&fifo->mRear); - int32_t front = fifo->mFront; - size_t availToRead = audio_utils_fifo_diff(fifo, rear, front); - return availToRead; -} diff --git a/LoopbackApp/app/src/main/jni/audio_utils/fifo.h b/LoopbackApp/app/src/main/jni/audio_utils/fifo.h deleted file mode 100644 index 37a9df8..0000000 --- a/LoopbackApp/app/src/main/jni/audio_utils/fifo.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_AUDIO_FIFO_H -#define ANDROID_AUDIO_FIFO_H - -#include <stdlib.h> - -// FIXME use atomic_int_least32_t and new atomic operations instead of legacy Android ones -// #include <stdatomic.h> - -#ifdef __cplusplus -extern "C" { -#endif - -// Single writer, single reader non-blocking FIFO. -// Writer and reader must be in same process. - -// No user-serviceable parts within. -struct audio_utils_fifo { - // These fields are const after initialization - size_t mFrameCount; // max number of significant frames to be stored in the FIFO > 0 - size_t mFrameCountP2; // roundup(mFrameCount) - size_t mFudgeFactor; // mFrameCountP2 - mFrameCount, the number of "wasted" frames after - // the end of mBuffer. Only the indices are wasted, not any memory. - size_t mFrameSize; // size of each frame in bytes - void *mBuffer; // pointer to caller-allocated buffer of size mFrameCount frames - - volatile int32_t mFront; // frame index of first frame slot available to read, or read index - volatile int32_t mRear; // frame index of next frame slot available to write, or write index -}; - -// Initialize a FIFO object. -// Input parameters: -// fifo Pointer to the FIFO object. -// frameCount Max number of significant frames to be stored in the FIFO > 0. -// If writes and reads always use the same count, and that count is a divisor of -// frameCount, then the writes and reads will never do a partial transfer. -// frameSize Size of each frame in bytes. -// buffer Pointer to a caller-allocated buffer of frameCount frames. -void audio_utils_fifo_init(struct audio_utils_fifo *fifo, size_t frameCount, size_t frameSize, - void *buffer); - -// De-initialize a FIFO object. -// Input parameters: -// fifo Pointer to the FIFO object. -void audio_utils_fifo_deinit(struct audio_utils_fifo *fifo); - -// Write to FIFO. -// Input parameters: -// fifo Pointer to the FIFO object. -// buffer Pointer to source buffer containing 'count' frames of data. -// Returns actual number of frames written <= count. -// The actual transfer count may be zero if the FIFO is full, -// or partial if the FIFO was almost full. -// A negative return value indicates an error. Currently there are no errors defined. -ssize_t audio_utils_fifo_write(struct audio_utils_fifo *fifo, const void *buffer, size_t count); - -// Read from FIFO. -// Input parameters: -// fifo Pointer to the FIFO object. -// buffer Pointer to destination buffer to be filled with up to 'count' frames of data. -// Returns actual number of frames read <= count. -// The actual transfer count may be zero if the FIFO is empty, -// or partial if the FIFO was almost empty. -// A negative return value indicates an error. Currently there are no errors defined. -ssize_t audio_utils_fifo_read(struct audio_utils_fifo *fifo, void *buffer, size_t count); - -size_t audio_utils_fifo_availToRead(struct audio_utils_fifo *fifo); - -#ifdef __cplusplus -} -#endif - -#endif // !ANDROID_AUDIO_FIFO_H diff --git a/LoopbackApp/app/src/main/jni/audio_utils/roundup.c b/LoopbackApp/app/src/main/jni/audio_utils/roundup.c deleted file mode 100644 index 6c8e504..0000000 --- a/LoopbackApp/app/src/main/jni/audio_utils/roundup.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -#include "roundup.h" - -unsigned roundup(unsigned v) { - // __builtin_clz is undefined for zero input - if (v == 0) { - v = 1; - } - - int lz = __builtin_clz((int) v); - unsigned rounded = ((unsigned) 0x80000000) >> lz; - // 0x800000001 and higher are actually rounded _down_ to prevent overflow - if (v > rounded && lz > 0) { - rounded <<= 1; - } - return rounded; -} diff --git a/LoopbackApp/app/src/main/jni/audio_utils/roundup.h b/LoopbackApp/app/src/main/jni/audio_utils/roundup.h deleted file mode 100644 index ad34289..0000000 --- a/LoopbackApp/app/src/main/jni/audio_utils/roundup.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -#ifndef ANDROID_AUDIO_ROUNDUP_H -#define ANDROID_AUDIO_ROUNDUP_H - -#ifdef __cplusplus -extern "C" { -#endif - -// Round up to the next highest power of 2 -unsigned roundup(unsigned v); - -#ifdef __cplusplus -} -#endif - -#endif // ANDROID_AUDIO_ROUNDUP_H diff --git a/LoopbackApp/app/src/main/jni/jni_sles.c b/LoopbackApp/app/src/main/jni/jni_sles.c deleted file mode 100644 index 0417252..0000000 --- a/LoopbackApp/app/src/main/jni/jni_sles.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * 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. - */ - -#include <android/log.h> -#include "sles.h" -#include "jni_sles.h" -#include <stdio.h> - - -JNIEXPORT jlong JNICALL Java_org_drrickorang_loopback_NativeAudioThread_slesInit - (JNIEnv *env, jobject obj __unused, jint samplingRate, jint frameCount, jint micSource, - jint performanceMode, - jint testType, jdouble frequency1, jobject byteBuffer, jshortArray loopbackTone, - jint maxRecordedLateCallbacks, jint ignoreFirstFrames) { - - sles_data * pSles = NULL; - - char* byteBufferPtr = (*env)->GetDirectBufferAddress(env, byteBuffer); - int byteBufferLength = (*env)->GetDirectBufferCapacity(env, byteBuffer); - - short* loopbackToneArray = (*env)->GetShortArrayElements(env, loopbackTone, 0); - - if (slesInit(&pSles, samplingRate, frameCount, micSource, - performanceMode, - testType, frequency1, byteBufferPtr, byteBufferLength, - loopbackToneArray, maxRecordedLateCallbacks, ignoreFirstFrames) != SLES_FAIL) { - return (long) pSles; - } - - // FIXME This should be stored as a (long) field in the object, - // so that incorrect Java code could not synthesize a bad sles pointer. - return 0; -} - - -JNIEXPORT jint JNICALL Java_org_drrickorang_loopback_NativeAudioThread_slesProcessNext -(JNIEnv *env __unused, jobject obj __unused, jlong sles, jdoubleArray samplesArray, jlong offset) { - sles_data * pSles = (sles_data*) (size_t) sles; - - long maxSamples = (*env)->GetArrayLength(env, samplesArray); - double *pSamples = (*env)->GetDoubleArrayElements(env, samplesArray, 0); - - long availableSamples = maxSamples-offset; - double *pCurrentSample = pSamples+offset; - - SLES_PRINTF("jni slesProcessNext pSles:%p, currentSample %p, availableSamples %ld ", - pSles, pCurrentSample, availableSamples); - - int samplesRead = slesProcessNext(pSles, pCurrentSample, availableSamples); - return samplesRead; -} - - -JNIEXPORT jint JNICALL Java_org_drrickorang_loopback_NativeAudioThread_slesDestroy - (JNIEnv *env __unused, jobject obj __unused, jlong sles) { - sles_data * pSles = (sles_data*) (size_t) sles; - int status = slesDestroy(&pSles); - return status; -} - - -JNIEXPORT jintArray JNICALL - Java_org_drrickorang_loopback_NativeAudioThread_slesGetRecorderBufferPeriod - (JNIEnv *env, jobject obj __unused, jlong sles) { - sles_data * pSles = (sles_data*) (size_t) sles; - int* recorderBufferPeriod = slesGetRecorderBufferPeriod(pSles); - - // get the length = RANGE - jintArray result = (*env)->NewIntArray(env, RANGE); - (*env)->SetIntArrayRegion(env, result, 0, RANGE, recorderBufferPeriod); - - return result; -} - - -JNIEXPORT jint JNICALL - Java_org_drrickorang_loopback_NativeAudioThread_slesGetRecorderMaxBufferPeriod - (JNIEnv *env __unused, jobject obj __unused, jlong sles) { - sles_data * pSles = (sles_data*) (size_t) sles; - int recorderMaxBufferPeriod = slesGetRecorderMaxBufferPeriod(pSles); - - return recorderMaxBufferPeriod; -} - - -JNIEXPORT jdouble JNICALL - Java_org_drrickorang_loopback_NativeAudioThread_slesGetRecorderVarianceBufferPeriod - (JNIEnv *env __unused, jobject obj __unused, jlong sles) { - sles_data *pSles = (sles_data *) (size_t) sles; - int64_t result = slesGetRecorderVarianceBufferPeriod(pSles); - // variance has units ns^2 so we have to square the conversion factor - double scaled = (double) result / ((double) NANOS_PER_MILLI * (double) NANOS_PER_MILLI); - return scaled; -} - - -JNIEXPORT jintArray -JNICALL Java_org_drrickorang_loopback_NativeAudioThread_slesGetPlayerBufferPeriod - (JNIEnv *env __unused, jobject obj __unused, jlong sles) { - sles_data * pSles = (sles_data*) (size_t) sles; - int* playerBufferPeriod = slesGetPlayerBufferPeriod(pSles); - - jintArray result = (*env)->NewIntArray(env, RANGE); - (*env)->SetIntArrayRegion(env, result, 0, RANGE, playerBufferPeriod); - - return result; -} - - -JNIEXPORT jint JNICALL - Java_org_drrickorang_loopback_NativeAudioThread_slesGetPlayerMaxBufferPeriod - (JNIEnv *env __unused, jobject obj __unused, jlong sles) { - sles_data * pSles = (sles_data*) (size_t) sles; - int playerMaxBufferPeriod = slesGetPlayerMaxBufferPeriod(pSles); - - return playerMaxBufferPeriod; -} - - -JNIEXPORT jdouble JNICALL -Java_org_drrickorang_loopback_NativeAudioThread_slesGetPlayerVarianceBufferPeriod - (JNIEnv *env __unused, jobject obj __unused, jlong sles) { - sles_data *pSles = (sles_data *) (size_t) sles; - int64_t result = slesGetPlayerVarianceBufferPeriod(pSles); - // variance has units ns^2 so we have to square the conversion factor - double scaled = (double) result / ((double) NANOS_PER_MILLI * (double) NANOS_PER_MILLI); - return scaled; -} - - -jobject getCallbackTimes(JNIEnv *env, callbackTimeStamps *callbacks, short expectedBufferPeriod){ - jintArray timeStamps = (*env)->NewIntArray(env, callbacks->index); - (*env)->SetIntArrayRegion(env, timeStamps, 0, callbacks->index, callbacks->timeStampsMs); - - jshortArray callbackLengths = (*env)->NewShortArray(env, callbacks->index); - (*env)->SetShortArrayRegion(env, callbackLengths, 0, callbacks->index, - callbacks->callbackDurations); - - jclass cls = (*env)->FindClass(env, "org/drrickorang/loopback/BufferCallbackTimes"); - jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "([I[SZS)V"); - jobject callbackTimes=(*env)->NewObject(env,cls, methodID, timeStamps, callbackLengths, - callbacks->exceededCapacity, expectedBufferPeriod); - return callbackTimes; -} - -JNIEXPORT jobject -JNICALL Java_org_drrickorang_loopback_NativeAudioThread_slesGetPlayerCallbackTimeStamps - (JNIEnv *env, jobject obj __unused, jlong sles) { - sles_data * pSles = (sles_data*) (size_t) sles; - return getCallbackTimes(env, &(pSles->playerTimeStamps), pSles->expectedBufferPeriod); -} - -JNIEXPORT jobject -JNICALL Java_org_drrickorang_loopback_NativeAudioThread_slesGetRecorderCallbackTimeStamps - (JNIEnv *env, jobject obj __unused, jlong sles) { - sles_data * pSles = (sles_data*) (size_t) sles; - return getCallbackTimes(env, &(pSles->recorderTimeStamps), pSles->expectedBufferPeriod); -} - -JNIEXPORT jint -JNICALL Java_org_drrickorang_loopback_NativeAudioThread_slesGetCaptureRank - (JNIEnv *env __unused, jobject obj __unused, jlong sles) { - sles_data * pSles = (sles_data*) (size_t) sles; - return slesGetCaptureRank(pSles); -} diff --git a/LoopbackApp/app/src/main/jni/jni_sles.h b/LoopbackApp/app/src/main/jni/jni_sles.h deleted file mode 100644 index f25bd52..0000000 --- a/LoopbackApp/app/src/main/jni/jni_sles.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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. - */ - -#include <jni.h> - -#ifndef _Included_org_drrickorang_loopback_jni -#define _Included_org_drrickorang_loopback_jni -#ifdef __cplusplus -extern "C" { -#endif - - -//////////////////////// -////SLE -JNIEXPORT jlong JNICALL Java_org_drrickorang_loopback_NativeAudioThread_slesInit - (JNIEnv *, jobject, jint, jint, jint, jint, jint, jdouble, jobject byteBuffer, - jshortArray loopbackTone, jint maxRecordedLateCallbacks, jint ignoreFirstFrames); - -JNIEXPORT jint JNICALL Java_org_drrickorang_loopback_NativeAudioThread_slesProcessNext - (JNIEnv *, jobject, jlong, jdoubleArray, jlong); - -JNIEXPORT jint JNICALL Java_org_drrickorang_loopback_NativeAudioThread_slesDestroy - (JNIEnv *, jobject, jlong); - -JNIEXPORT jintArray JNICALL - Java_org_drrickorang_loopback_NativeAudioThread_slesGetRecorderBufferPeriod - (JNIEnv *, jobject, jlong); - -JNIEXPORT jint JNICALL - Java_org_drrickorang_loopback_NativeAudioThread_slesGetRecorderMaxBufferPeriod - (JNIEnv *, jobject, jlong); - -JNIEXPORT jdouble JNICALL - Java_org_drrickorang_loopback_NativeAudioThread_slesGetRecorderVarianceBufferPeriod - (JNIEnv *, jobject, jlong); - -JNIEXPORT jintArray JNICALL - Java_org_drrickorang_loopback_NativeAudioThread_slesGetPlayerBufferPeriod - (JNIEnv *, jobject, jlong); - -JNIEXPORT jint JNICALL - Java_org_drrickorang_loopback_NativeAudioThread_slesGetPlayerMaxBufferPeriod - (JNIEnv *, jobject, jlong); - -JNIEXPORT jdouble JNICALL - Java_org_drrickorang_loopback_NativeAudioThread_slesGetPlayerVarianceBufferPeriod - (JNIEnv *, jobject, jlong); - -JNIEXPORT jint JNICALL - Java_org_drrickorang_loopback_NativeAudioThread_slesGetCaptureRank - (JNIEnv *, jobject, jlong); - -#ifdef __cplusplus -} -#endif -#endif //_Included_org_drrickorang_loopback_jni diff --git a/LoopbackApp/app/src/main/jni/sles.cpp b/LoopbackApp/app/src/main/jni/sles.cpp deleted file mode 100644 index 159269b..0000000 --- a/LoopbackApp/app/src/main/jni/sles.cpp +++ /dev/null @@ -1,1051 +0,0 @@ -/* - * 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; - } -} diff --git a/LoopbackApp/app/src/main/jni/sles.h b/LoopbackApp/app/src/main/jni/sles.h deleted file mode 100644 index c176656..0000000 --- a/LoopbackApp/app/src/main/jni/sles.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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. - */ - -#include <SLES/OpenSLES.h> -#include <SLES/OpenSLES_Android.h> -#include <pthread.h> -#include <android/log.h> -#include <jni.h> -#include <stdbool.h> - -#ifndef _Included_org_drrickorang_loopback_sles -#define _Included_org_drrickorang_loopback_sles - -//struct audio_utils_fifo; -#define SLES_PRINTF(...) __android_log_print(ANDROID_LOG_INFO, "sles_jni", __VA_ARGS__); - - -#ifdef __cplusplus -extern "C" { -#endif -#include <audio_utils/fifo.h> - -typedef struct { - int* timeStampsMs; // Array of milliseconds since first callback - short* callbackDurations; // Array of milliseconds between callback and previous callback - short index; // Current write position - struct timespec startTime; // Time of first callback {seconds,nanoseconds} - int capacity; // Total number of callback times/lengths that can be recorded - bool exceededCapacity; // Set only if late callbacks come after array is full -} callbackTimeStamps; - -typedef struct { - int* buffer_period; - struct timespec previous_time; - struct timespec current_time; - int buffer_count; - int max_buffer_period; - - volatile int32_t captureRank; // Set > 0 when the callback requests a systrace/bug report - - int measurement_count; // number of measurements which were actually recorded - int64_t SDM; // sum of squares of deviations from the expected mean - int64_t var; // variance in nanoseconds^2 -} bufferStats; - -//TODO fix this -typedef struct { - SLuint32 rxBufCount; // -r# - SLuint32 txBufCount; // -t# - SLuint32 bufSizeInFrames; // -f# - SLuint32 channels; // -c# - SLuint32 sampleRate; // -s# - SLuint32 exitAfterSeconds; // -e# - SLuint32 freeBufCount; // calculated - SLuint32 bufSizeInBytes; // calculated - int injectImpulse; // -i#i - size_t totalDiscardedInputFrames; // total number of input frames discarded - int ignoreFirstFrames; - - // 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 - - struct audio_utils_fifo fifo; // jitter buffer between recorder and player callbacks, - // to mitigate unpredictable phase difference between these, - // or even concurrent callbacks on two CPU cores - struct audio_utils_fifo fifo2; // For sending data to java code (to plot it) - short *fifo2Buffer; - short *fifoBuffer; - SLAndroidSimpleBufferQueueItf recorderBufferQueue; - SLBufferQueueItf playerBufferQueue; - - //other things that belong here - SLObjectItf playerObject; - SLObjectItf recorderObject; - SLObjectItf outputmixObject; - SLObjectItf engineObject; - - bufferStats recorderBufferStats; - bufferStats playerBufferStats; - - int testType; - double frequency1; - double bufferTestPhase1; - int count; - char* byteBufferPtr; - int byteBufferLength; - - short* loopbackTone; - - callbackTimeStamps recorderTimeStamps; - callbackTimeStamps playerTimeStamps; - short expectedBufferPeriod; -} sles_data; - -#define NANOS_PER_SECOND 1000000000 -#define NANOS_PER_MILLI 1000000 -#define MILLIS_PER_SECOND 1000 - -// how late in ms a callback must be to trigger a systrace/bugreport -#define LATE_CALLBACK_CAPTURE_THRESHOLD 4 -#define LATE_CALLBACK_OUTLIER_THRESHOLD 1 -#define BUFFER_PERIOD_DISCARD 10 -#define BUFFER_PERIOD_DISCARD_FULL_DUPLEX_PARTNER 2 - -enum { - SLES_SUCCESS = 0, - SLES_FAIL = 1, - RANGE = 1002, - TEST_TYPE_LATENCY = 222, - TEST_TYPE_BUFFER_PERIOD = 223 -} SLES_STATUS_ENUM; - -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); - -//note the double pointer to properly free the memory of the structure -int slesDestroy(sles_data ** ppSles); - - -///full -int slesFull(sles_data *pSles); - -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 slesProcessNext(sles_data *pSles, double *pSamples, long maxSamples); -int slesDestroyServer(sles_data *pSles); -int* slesGetRecorderBufferPeriod(sles_data *pSles); -int slesGetRecorderMaxBufferPeriod(sles_data *pSles); -int64_t slesGetRecorderVarianceBufferPeriod(sles_data *pSles); -int* slesGetPlayerBufferPeriod(sles_data *pSles); -int slesGetPlayerMaxBufferPeriod(sles_data *pSles); -int64_t slesGetPlayerVarianceBufferPeriod(sles_data *pSles); -int slesGetCaptureRank(sles_data *pSles); - -void initBufferStats(bufferStats *stats); -void collectBufferPeriod(bufferStats *stats, bufferStats *fdpStats, callbackTimeStamps *timeStamps, - short expectedBufferPeriod); -bool updateBufferStats(bufferStats *stats, int64_t diff_in_nano, int expectedBufferPeriod); -void recordTimeStamp(callbackTimeStamps *timeStamps, - int64_t callbackDuration, int64_t timeStamp); - -ssize_t byteBuffer_write(sles_data *pSles, char *buffer, size_t count); - -#ifdef __cplusplus -} -#endif -#endif //_Included_org_drrickorang_loopback_sles |