/* * 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_native.h" #include #include #include "loopback.h" #define LOG_TAG "jni_native" static int nativeEngineFromThreadType(int threadType) { switch (threadType) { case AUDIO_THREAD_TYPE_NATIVE_SLES: return NATIVE_ENGINE_SLES; case AUDIO_THREAD_TYPE_NATIVE_AAUDIO: return NATIVE_ENGINE_AAUDIO; } __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "unsupported thread type %d", threadType); return -1; } JNIEXPORT jobject JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeComputeDefaultSettings (JNIEnv *env, jobject obj __unused, jint bytesPerFrame, jint threadType, jint performanceMode) { int engine = nativeEngineFromThreadType(threadType); if (engine == -1) return NULL; int samplingRate, playerBufferFrameCount, recorderBufferFrameCount; if (sEngines[engine].computeDefaultSettings(performanceMode, &samplingRate, &playerBufferFrameCount, &recorderBufferFrameCount) == STATUS_SUCCESS) { jclass cls = (*env)->FindClass(env, "org/drrickorang/loopback/TestSettings"); jmethodID methodID = (*env)->GetMethodID(env, cls, "", "(III)V"); jobject testSettings = (*env)->NewObject(env, cls, methodID, samplingRate, playerBufferFrameCount * bytesPerFrame, recorderBufferFrameCount * bytesPerFrame); return testSettings; } else { return NULL; } } JNIEXPORT jlong JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeInit (JNIEnv *env, jobject obj __unused, jint threadType, jint samplingRate, jint frameCount, jint micSource, jint performanceMode, jint testType, jdouble frequency1, jobject byteBuffer, jshortArray loopbackTone, jint maxRecordedLateCallbacks, jint ignoreFirstFrames) { int engine = nativeEngineFromThreadType(threadType); if (engine == -1) return 0; native_engine_instance_t *pInstance = (native_engine_instance_t*) malloc(sizeof(native_engine_instance_t)); if (pInstance == NULL) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "failed to allocate a native engine instance"); return 0; } void *pContext = NULL; char *byteBufferPtr = (*env)->GetDirectBufferAddress(env, byteBuffer); int byteBufferLength = (*env)->GetDirectBufferCapacity(env, byteBuffer); short *loopbackToneArray = (*env)->GetShortArrayElements(env, loopbackTone, 0); if (sEngines[engine].init(&pContext, samplingRate, frameCount, micSource, performanceMode, testType, frequency1, byteBufferPtr, byteBufferLength, loopbackToneArray, maxRecordedLateCallbacks, ignoreFirstFrames) != STATUS_FAIL) { pInstance->context = pContext; pInstance->methods = &sEngines[engine]; return (long) pInstance; } free(pInstance); return 0; } JNIEXPORT jint JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeProcessNext (JNIEnv *env __unused, jobject obj __unused, jlong handle, jdoubleArray samplesArray, jlong offset) { native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; long maxSamples = (*env)->GetArrayLength(env, samplesArray); double *pSamples = (*env)->GetDoubleArrayElements(env, samplesArray, 0); long availableSamples = maxSamples-offset; double *pCurrentSample = pSamples+offset; __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "jni nativeProcessNext currentSample %p, availableSamples %ld ", pCurrentSample, availableSamples); int samplesRead = pInstance->methods->processNext( pInstance->context, pCurrentSample, availableSamples); return samplesRead; } JNIEXPORT jint JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeDestroy (JNIEnv *env __unused, jobject obj __unused, jlong handle) { native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; int status = pInstance->methods->destroy(&pInstance->context); free(pInstance); return status; } JNIEXPORT jintArray JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderBufferPeriod (JNIEnv *env, jobject obj __unused, jlong handle) { native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; int* recorderBufferPeriod = pInstance->methods->getRecorderBufferPeriod( pInstance->context); // 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_nativeGetRecorderMaxBufferPeriod (JNIEnv *env __unused, jobject obj __unused, jlong handle) { native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; int recorderMaxBufferPeriod = pInstance->methods->getRecorderMaxBufferPeriod( pInstance->context); return recorderMaxBufferPeriod; } JNIEXPORT jdouble JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderVarianceBufferPeriod (JNIEnv *env __unused, jobject obj __unused, jlong handle) { native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; int64_t result = pInstance->methods->getRecorderVarianceBufferPeriod(pInstance->context); // 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_nativeGetPlayerBufferPeriod (JNIEnv *env __unused, jobject obj __unused, jlong handle) { native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; int* playerBufferPeriod = pInstance->methods->getPlayerBufferPeriod(pInstance->context); jintArray result = (*env)->NewIntArray(env, RANGE); (*env)->SetIntArrayRegion(env, result, 0, RANGE, playerBufferPeriod); return result; } JNIEXPORT jint JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerMaxBufferPeriod (JNIEnv *env __unused, jobject obj __unused, jlong handle) { native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; int playerMaxBufferPeriod = pInstance->methods->getPlayerMaxBufferPeriod(pInstance->context); return playerMaxBufferPeriod; } JNIEXPORT jdouble JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerVarianceBufferPeriod (JNIEnv *env __unused, jobject obj __unused, jlong handle) { native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; int64_t result = pInstance->methods->getPlayerVarianceBufferPeriod(pInstance->context); // 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, "", "([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_nativeGetPlayerCallbackTimeStamps (JNIEnv *env, jobject obj __unused, jlong handle) { native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; callbackTimeStamps *pTSs; int expectedBufferPeriod = pInstance->methods->getPlayerTimeStampsAndExpectedBufferPeriod( pInstance->context, &pTSs); return getCallbackTimes(env, pTSs, expectedBufferPeriod); } JNIEXPORT jobject JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderCallbackTimeStamps (JNIEnv *env, jobject obj __unused, jlong handle) { native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; callbackTimeStamps *pTSs; int expectedBufferPeriod = pInstance->methods->getRecorderTimeStampsAndExpectedBufferPeriod( pInstance->context, &pTSs); return getCallbackTimes(env, pTSs, expectedBufferPeriod); } JNIEXPORT jint JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetCaptureRank (JNIEnv *env __unused, jobject obj __unused, jlong handle) { native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; return pInstance->methods->getCaptureRank(pInstance->context); }