1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "jni_native.h" 18 19 #include <stdlib.h> 20 21 #include <android/log.h> 22 23 #include "loopback.h" 24 25 #define LOG_TAG "jni_native" 26 27 static int nativeEngineFromThreadType(int threadType) { 28 switch (threadType) { 29 case AUDIO_THREAD_TYPE_NATIVE_SLES: return NATIVE_ENGINE_SLES; 30 case AUDIO_THREAD_TYPE_NATIVE_AAUDIO: return NATIVE_ENGINE_AAUDIO; 31 } 32 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, 33 "unsupported thread type %d", threadType); 34 return -1; 35 } 36 37 JNIEXPORT jobject JNICALL 38 Java_org_drrickorang_loopback_NativeAudioThread_nativeComputeDefaultSettings 39 (JNIEnv *env, jobject obj __unused, jint bytesPerFrame, jint threadType, jint performanceMode) { 40 int engine = nativeEngineFromThreadType(threadType); 41 if (engine == -1) return NULL; 42 int samplingRate, playerBufferFrameCount, recorderBufferFrameCount; 43 if (sEngines[engine].computeDefaultSettings(performanceMode, &samplingRate, 44 &playerBufferFrameCount, &recorderBufferFrameCount) == STATUS_SUCCESS) { 45 jclass cls = (*env)->FindClass(env, "org/drrickorang/loopback/TestSettings"); 46 jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "(III)V"); 47 jobject testSettings = (*env)->NewObject(env, cls, methodID, 48 samplingRate, 49 playerBufferFrameCount * bytesPerFrame, 50 recorderBufferFrameCount * bytesPerFrame); 51 return testSettings; 52 } else { 53 return NULL; 54 } 55 } 56 57 JNIEXPORT jlong JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeInit 58 (JNIEnv *env, jobject obj __unused, jint threadType, jint samplingRate, jint frameCount, 59 jint micSource, jint performanceMode, 60 jint testType, jdouble frequency1, jobject byteBuffer, jshortArray loopbackTone, 61 jint maxRecordedLateCallbacks, jint ignoreFirstFrames) { 62 63 int engine = nativeEngineFromThreadType(threadType); 64 if (engine == -1) return 0; 65 66 native_engine_instance_t *pInstance = 67 (native_engine_instance_t*) malloc(sizeof(native_engine_instance_t)); 68 if (pInstance == NULL) { 69 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, 70 "failed to allocate a native engine instance"); 71 return 0; 72 } 73 void *pContext = NULL; 74 75 char *byteBufferPtr = (*env)->GetDirectBufferAddress(env, byteBuffer); 76 int byteBufferLength = (*env)->GetDirectBufferCapacity(env, byteBuffer); 77 78 short *loopbackToneArray = (*env)->GetShortArrayElements(env, loopbackTone, 0); 79 80 if (sEngines[engine].init(&pContext, samplingRate, frameCount, micSource, 81 performanceMode, 82 testType, frequency1, byteBufferPtr, byteBufferLength, 83 loopbackToneArray, maxRecordedLateCallbacks, ignoreFirstFrames) != STATUS_FAIL) { 84 pInstance->context = pContext; 85 pInstance->methods = &sEngines[engine]; 86 return (long) pInstance; 87 } 88 89 free(pInstance); 90 return 0; 91 } 92 93 94 JNIEXPORT jint JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeProcessNext 95 (JNIEnv *env __unused, jobject obj __unused, jlong handle, jdoubleArray samplesArray, 96 jlong offset) { 97 native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; 98 99 long maxSamples = (*env)->GetArrayLength(env, samplesArray); 100 double *pSamples = (*env)->GetDoubleArrayElements(env, samplesArray, 0); 101 102 long availableSamples = maxSamples-offset; 103 double *pCurrentSample = pSamples+offset; 104 105 __android_log_print(ANDROID_LOG_INFO, LOG_TAG, 106 "jni nativeProcessNext currentSample %p, availableSamples %ld ", 107 pCurrentSample, availableSamples); 108 109 int samplesRead = pInstance->methods->processNext( 110 pInstance->context, pCurrentSample, availableSamples); 111 return samplesRead; 112 } 113 114 115 JNIEXPORT jint JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeDestroy 116 (JNIEnv *env __unused, jobject obj __unused, jlong handle) { 117 native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; 118 int status = pInstance->methods->destroy(&pInstance->context); 119 free(pInstance); 120 return status; 121 } 122 123 124 JNIEXPORT jintArray JNICALL 125 Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderBufferPeriod 126 (JNIEnv *env, jobject obj __unused, jlong handle) { 127 native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; 128 int* recorderBufferPeriod = pInstance->methods->getRecorderBufferPeriod( 129 pInstance->context); 130 131 // get the length = RANGE 132 jintArray result = (*env)->NewIntArray(env, RANGE); 133 (*env)->SetIntArrayRegion(env, result, 0, RANGE, recorderBufferPeriod); 134 135 return result; 136 } 137 138 139 JNIEXPORT jint JNICALL 140 Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderMaxBufferPeriod 141 (JNIEnv *env __unused, jobject obj __unused, jlong handle) { 142 native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; 143 int recorderMaxBufferPeriod = pInstance->methods->getRecorderMaxBufferPeriod( 144 pInstance->context); 145 146 return recorderMaxBufferPeriod; 147 } 148 149 150 JNIEXPORT jdouble JNICALL 151 Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderVarianceBufferPeriod 152 (JNIEnv *env __unused, jobject obj __unused, jlong handle) { 153 native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; 154 int64_t result = pInstance->methods->getRecorderVarianceBufferPeriod(pInstance->context); 155 // variance has units ns^2 so we have to square the conversion factor 156 double scaled = (double) result / ((double) NANOS_PER_MILLI * (double) NANOS_PER_MILLI); 157 return scaled; 158 } 159 160 161 JNIEXPORT jintArray 162 JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerBufferPeriod 163 (JNIEnv *env __unused, jobject obj __unused, jlong handle) { 164 native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; 165 int* playerBufferPeriod = pInstance->methods->getPlayerBufferPeriod(pInstance->context); 166 167 jintArray result = (*env)->NewIntArray(env, RANGE); 168 (*env)->SetIntArrayRegion(env, result, 0, RANGE, playerBufferPeriod); 169 170 return result; 171 } 172 173 174 JNIEXPORT jint JNICALL 175 Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerMaxBufferPeriod 176 (JNIEnv *env __unused, jobject obj __unused, jlong handle) { 177 native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; 178 int playerMaxBufferPeriod = pInstance->methods->getPlayerMaxBufferPeriod(pInstance->context); 179 180 return playerMaxBufferPeriod; 181 } 182 183 184 JNIEXPORT jdouble JNICALL 185 Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerVarianceBufferPeriod 186 (JNIEnv *env __unused, jobject obj __unused, jlong handle) { 187 native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; 188 int64_t result = pInstance->methods->getPlayerVarianceBufferPeriod(pInstance->context); 189 // variance has units ns^2 so we have to square the conversion factor 190 double scaled = (double) result / ((double) NANOS_PER_MILLI * (double) NANOS_PER_MILLI); 191 return scaled; 192 } 193 194 195 jobject getCallbackTimes(JNIEnv *env, callbackTimeStamps *callbacks, short expectedBufferPeriod){ 196 jintArray timeStamps = (*env)->NewIntArray(env, callbacks->index); 197 (*env)->SetIntArrayRegion(env, timeStamps, 0, callbacks->index, callbacks->timeStampsMs); 198 199 jshortArray callbackLengths = (*env)->NewShortArray(env, callbacks->index); 200 (*env)->SetShortArrayRegion(env, callbackLengths, 0, callbacks->index, 201 callbacks->callbackDurations); 202 203 jclass cls = (*env)->FindClass(env, "org/drrickorang/loopback/BufferCallbackTimes"); 204 jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "([I[SZS)V"); 205 jobject callbackTimes=(*env)->NewObject(env,cls, methodID, timeStamps, callbackLengths, 206 callbacks->exceededCapacity, expectedBufferPeriod); 207 return callbackTimes; 208 } 209 210 JNIEXPORT jobject 211 JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerCallbackTimeStamps 212 (JNIEnv *env, jobject obj __unused, jlong handle) { 213 native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; 214 callbackTimeStamps *pTSs; 215 int expectedBufferPeriod = pInstance->methods->getPlayerTimeStampsAndExpectedBufferPeriod( 216 pInstance->context, &pTSs); 217 return getCallbackTimes(env, pTSs, expectedBufferPeriod); 218 } 219 220 JNIEXPORT jobject 221 JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderCallbackTimeStamps 222 (JNIEnv *env, jobject obj __unused, jlong handle) { 223 native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; 224 callbackTimeStamps *pTSs; 225 int expectedBufferPeriod = pInstance->methods->getRecorderTimeStampsAndExpectedBufferPeriod( 226 pInstance->context, &pTSs); 227 return getCallbackTimes(env, pTSs, expectedBufferPeriod); 228 } 229 230 JNIEXPORT jint 231 JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetCaptureRank 232 (JNIEnv *env __unused, jobject obj __unused, jlong handle) { 233 native_engine_instance_t *pInstance = (native_engine_instance_t*) handle; 234 return pInstance->methods->getCaptureRank(pInstance->context); 235 } 236