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