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 
nativeEngineFromThreadType(int threadType)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
Java_org_drrickorang_loopback_NativeAudioThread_nativeComputeDefaultSettings(JNIEnv * env,jobject obj __unused,jint bytesPerFrame,jint threadType,jint performanceMode)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 
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)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 
Java_org_drrickorang_loopback_NativeAudioThread_nativeProcessNext(JNIEnv * env __unused,jobject obj __unused,jlong handle,jdoubleArray samplesArray,jlong offset)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 
Java_org_drrickorang_loopback_NativeAudioThread_nativeDestroy(JNIEnv * env __unused,jobject obj __unused,jlong handle)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
Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderBufferPeriod(JNIEnv * env,jobject obj __unused,jlong handle)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
Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderMaxBufferPeriod(JNIEnv * env __unused,jobject obj __unused,jlong handle)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
Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderVarianceBufferPeriod(JNIEnv * env __unused,jobject obj __unused,jlong handle)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
Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerBufferPeriod(JNIEnv * env __unused,jobject obj __unused,jlong handle)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
Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerMaxBufferPeriod(JNIEnv * env __unused,jobject obj __unused,jlong handle)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
Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerVarianceBufferPeriod(JNIEnv * env __unused,jobject obj __unused,jlong handle)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 
getCallbackTimes(JNIEnv * env,callbackTimeStamps * callbacks,short expectedBufferPeriod)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
Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerCallbackTimeStamps(JNIEnv * env,jobject obj __unused,jlong handle)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
Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderCallbackTimeStamps(JNIEnv * env,jobject obj __unused,jlong handle)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
Java_org_drrickorang_loopback_NativeAudioThread_nativeGetCaptureRank(JNIEnv * env __unused,jobject obj __unused,jlong handle)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