1 /*
2 * Copyright (C) 2010 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 <stdio.h>
18
19 //#define LOG_NDEBUG 0
20 #define LOG_TAG "visualizers-JNI"
21
22 #include <utils/Log.h>
23 #include <nativehelper/jni.h>
24 #include <nativehelper/JNIHelp.h>
25 #include <android_runtime/AndroidRuntime.h>
26 #include "media/Visualizer.h"
27
28 using namespace android;
29
30 #define VISUALIZER_SUCCESS 0
31 #define VISUALIZER_ERROR -1
32 #define VISUALIZER_ERROR_ALREADY_EXISTS -2
33 #define VISUALIZER_ERROR_NO_INIT -3
34 #define VISUALIZER_ERROR_BAD_VALUE -4
35 #define VISUALIZER_ERROR_INVALID_OPERATION -5
36 #define VISUALIZER_ERROR_NO_MEMORY -6
37 #define VISUALIZER_ERROR_DEAD_OBJECT -7
38
39 #define NATIVE_EVENT_PCM_CAPTURE 0
40 #define NATIVE_EVENT_FFT_CAPTURE 1
41
42 // ----------------------------------------------------------------------------
43 static const char* const kClassPathName = "android/media/audiofx/Visualizer";
44
45 struct fields_t {
46 // these fields provide access from C++ to the...
47 jclass clazzEffect; // Visualizer class
48 jmethodID midPostNativeEvent; // event post callback method
49 jfieldID fidNativeVisualizer; // stores in Java the native Visualizer object
50 jfieldID fidJniData; // stores in Java additional resources used by the native Visualizer
51 };
52 static fields_t fields;
53
54 struct visualizer_callback_cookie {
55 jclass visualizer_class; // Visualizer class
56 jobject visualizer_ref; // Visualizer object instance
57 };
58
59 // ----------------------------------------------------------------------------
60 class visualizerJniStorage {
61 public:
62 visualizer_callback_cookie mCallbackData;
63
visualizerJniStorage()64 visualizerJniStorage() {
65 }
66
~visualizerJniStorage()67 ~visualizerJniStorage() {
68 }
69
70 };
71
72
translateError(int code)73 static jint translateError(int code) {
74 switch(code) {
75 case NO_ERROR:
76 return VISUALIZER_SUCCESS;
77 case ALREADY_EXISTS:
78 return VISUALIZER_ERROR_ALREADY_EXISTS;
79 case NO_INIT:
80 return VISUALIZER_ERROR_NO_INIT;
81 case BAD_VALUE:
82 return VISUALIZER_ERROR_BAD_VALUE;
83 case INVALID_OPERATION:
84 return VISUALIZER_ERROR_INVALID_OPERATION;
85 case NO_MEMORY:
86 return VISUALIZER_ERROR_NO_MEMORY;
87 case DEAD_OBJECT:
88 return VISUALIZER_ERROR_DEAD_OBJECT;
89 default:
90 return VISUALIZER_ERROR;
91 }
92 }
93
94
95 // ----------------------------------------------------------------------------
captureCallback(void * user,uint32_t waveformSize,uint8_t * waveform,uint32_t fftSize,uint8_t * fft,uint32_t samplingrate)96 static void captureCallback(void* user,
97 uint32_t waveformSize,
98 uint8_t *waveform,
99 uint32_t fftSize,
100 uint8_t *fft,
101 uint32_t samplingrate) {
102
103 int arg1 = 0;
104 int arg2 = 0;
105 size_t size;
106
107 visualizer_callback_cookie *callbackInfo = (visualizer_callback_cookie *)user;
108 JNIEnv *env = AndroidRuntime::getJNIEnv();
109
110 LOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
111 callbackInfo,
112 callbackInfo->visualizer_ref,
113 callbackInfo->visualizer_class);
114
115 if (!user || !env) {
116 LOGW("captureCallback error user %p, env %p", user, env);
117 return;
118 }
119
120 if (waveformSize != 0 && waveform != NULL) {
121 jbyteArray jArray = env->NewByteArray(waveformSize);
122 if (jArray != NULL) {
123 jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
124 memcpy(nArray, waveform, waveformSize);
125 env->ReleaseByteArrayElements(jArray, nArray, 0);
126 env->CallStaticVoidMethod(
127 callbackInfo->visualizer_class,
128 fields.midPostNativeEvent,
129 callbackInfo->visualizer_ref,
130 NATIVE_EVENT_PCM_CAPTURE,
131 samplingrate,
132 0,
133 jArray);
134 env->DeleteLocalRef(jArray);
135 }
136 }
137
138 if (fftSize != 0 && fft != NULL) {
139 jbyteArray jArray = env->NewByteArray(fftSize);
140 if (jArray != NULL) {
141 jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
142 memcpy(nArray, fft, fftSize);
143 env->ReleaseByteArrayElements(jArray, nArray, 0);
144 env->CallStaticVoidMethod(
145 callbackInfo->visualizer_class,
146 fields.midPostNativeEvent,
147 callbackInfo->visualizer_ref,
148 NATIVE_EVENT_FFT_CAPTURE,
149 samplingrate,
150 0,
151 jArray);
152 env->DeleteLocalRef(jArray);
153 }
154 }
155
156 if (env->ExceptionCheck()) {
157 env->ExceptionDescribe();
158 env->ExceptionClear();
159 }
160 }
161
getVisualizer(JNIEnv * env,jobject thiz)162 static Visualizer *getVisualizer(JNIEnv* env, jobject thiz)
163 {
164 Visualizer *v = (Visualizer *)env->GetIntField(
165 thiz, fields.fidNativeVisualizer);
166 if (v == NULL) {
167 jniThrowException(env, "java/lang/IllegalStateException",
168 "Unable to retrieve Visualizer pointer");
169 }
170 return v;
171 }
172
173 // ----------------------------------------------------------------------------
174 // This function gets some field IDs, which in turn causes class initialization.
175 // It is called from a static block in Visualizer, which won't run until the
176 // first time an instance of this class is used.
177 static void
android_media_visualizer_native_init(JNIEnv * env)178 android_media_visualizer_native_init(JNIEnv *env)
179 {
180
181 LOGV("android_media_visualizer_native_init");
182
183 fields.clazzEffect = NULL;
184
185 // Get the Visualizer class
186 jclass clazz = env->FindClass(kClassPathName);
187 if (clazz == NULL) {
188 LOGE("Can't find %s", kClassPathName);
189 return;
190 }
191
192 fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);
193
194 // Get the postEvent method
195 fields.midPostNativeEvent = env->GetStaticMethodID(
196 fields.clazzEffect,
197 "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
198 if (fields.midPostNativeEvent == NULL) {
199 LOGE("Can't find Visualizer.%s", "postEventFromNative");
200 return;
201 }
202
203 // Get the variables fields
204 // nativeTrackInJavaObj
205 fields.fidNativeVisualizer = env->GetFieldID(
206 fields.clazzEffect,
207 "mNativeVisualizer", "I");
208 if (fields.fidNativeVisualizer == NULL) {
209 LOGE("Can't find Visualizer.%s", "mNativeVisualizer");
210 return;
211 }
212 // fidJniData;
213 fields.fidJniData = env->GetFieldID(
214 fields.clazzEffect,
215 "mJniData", "I");
216 if (fields.fidJniData == NULL) {
217 LOGE("Can't find Visualizer.%s", "mJniData");
218 return;
219 }
220
221 }
222
223
224 static jint
android_media_visualizer_native_setup(JNIEnv * env,jobject thiz,jobject weak_this,jint sessionId,jintArray jId)225 android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
226 jint sessionId, jintArray jId)
227 {
228 LOGV("android_media_visualizer_native_setup");
229 visualizerJniStorage* lpJniStorage = NULL;
230 int lStatus = VISUALIZER_ERROR_NO_MEMORY;
231 Visualizer* lpVisualizer = NULL;
232 jint* nId = NULL;
233
234 lpJniStorage = new visualizerJniStorage();
235 if (lpJniStorage == NULL) {
236 LOGE("setup: Error creating JNI Storage");
237 goto setup_failure;
238 }
239
240 lpJniStorage->mCallbackData.visualizer_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
241 // we use a weak reference so the Visualizer object can be garbage collected.
242 lpJniStorage->mCallbackData.visualizer_ref = env->NewGlobalRef(weak_this);
243
244 LOGV("setup: lpJniStorage: %p visualizer_ref %p visualizer_class %p, &mCallbackData %p",
245 lpJniStorage,
246 lpJniStorage->mCallbackData.visualizer_ref,
247 lpJniStorage->mCallbackData.visualizer_class,
248 &lpJniStorage->mCallbackData);
249
250 if (jId == NULL) {
251 LOGE("setup: NULL java array for id pointer");
252 lStatus = VISUALIZER_ERROR_BAD_VALUE;
253 goto setup_failure;
254 }
255
256 // create the native Visualizer object
257 lpVisualizer = new Visualizer(0,
258 NULL,
259 NULL,
260 sessionId);
261 if (lpVisualizer == NULL) {
262 LOGE("Error creating Visualizer");
263 goto setup_failure;
264 }
265
266 lStatus = translateError(lpVisualizer->initCheck());
267 if (lStatus != VISUALIZER_SUCCESS && lStatus != VISUALIZER_ERROR_ALREADY_EXISTS) {
268 LOGE("Visualizer initCheck failed %d", lStatus);
269 goto setup_failure;
270 }
271
272 nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
273 if (nId == NULL) {
274 LOGE("setup: Error retrieving id pointer");
275 lStatus = VISUALIZER_ERROR_BAD_VALUE;
276 goto setup_failure;
277 }
278 nId[0] = lpVisualizer->id();
279 env->ReleasePrimitiveArrayCritical(jId, nId, 0);
280 nId = NULL;
281
282 env->SetIntField(thiz, fields.fidNativeVisualizer, (int)lpVisualizer);
283
284 env->SetIntField(thiz, fields.fidJniData, (int)lpJniStorage);
285
286 return VISUALIZER_SUCCESS;
287
288 // failures:
289 setup_failure:
290
291 if (nId != NULL) {
292 env->ReleasePrimitiveArrayCritical(jId, nId, 0);
293 }
294
295 if (lpVisualizer) {
296 delete lpVisualizer;
297 }
298 env->SetIntField(thiz, fields.fidNativeVisualizer, 0);
299
300 if (lpJniStorage) {
301 delete lpJniStorage;
302 }
303 env->SetIntField(thiz, fields.fidJniData, 0);
304
305 return lStatus;
306 }
307
308 // ----------------------------------------------------------------------------
android_media_visualizer_native_finalize(JNIEnv * env,jobject thiz)309 static void android_media_visualizer_native_finalize(JNIEnv *env, jobject thiz) {
310 LOGV("android_media_visualizer_native_finalize jobject: %x\n", (int)thiz);
311
312 // delete the Visualizer object
313 Visualizer* lpVisualizer = (Visualizer *)env->GetIntField(
314 thiz, fields.fidNativeVisualizer);
315 if (lpVisualizer) {
316 LOGV("deleting Visualizer: %x\n", (int)lpVisualizer);
317 delete lpVisualizer;
318 }
319
320 // delete the JNI data
321 visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(
322 thiz, fields.fidJniData);
323 if (lpJniStorage) {
324 LOGV("deleting pJniStorage: %x\n", (int)lpJniStorage);
325 delete lpJniStorage;
326 }
327 }
328
329 // ----------------------------------------------------------------------------
android_media_visualizer_native_release(JNIEnv * env,jobject thiz)330 static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) {
331
332 // do everything a call to finalize would
333 android_media_visualizer_native_finalize(env, thiz);
334 // + reset the native resources in the Java object so any attempt to access
335 // them after a call to release fails.
336 env->SetIntField(thiz, fields.fidNativeVisualizer, 0);
337 env->SetIntField(thiz, fields.fidJniData, 0);
338 }
339
340 static jint
android_media_visualizer_native_setEnabled(JNIEnv * env,jobject thiz,jboolean enabled)341 android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
342 {
343 Visualizer* lpVisualizer = getVisualizer(env, thiz);
344 if (lpVisualizer == NULL) {
345 return VISUALIZER_ERROR_NO_INIT;
346 }
347
348 return translateError(lpVisualizer->setEnabled(enabled));
349 }
350
351 static jboolean
android_media_visualizer_native_getEnabled(JNIEnv * env,jobject thiz)352 android_media_visualizer_native_getEnabled(JNIEnv *env, jobject thiz)
353 {
354 Visualizer* lpVisualizer = getVisualizer(env, thiz);
355 if (lpVisualizer == NULL) {
356 return false;
357 }
358
359 return (jboolean)lpVisualizer->getEnabled();
360 }
361
362 static jintArray
android_media_visualizer_native_getCaptureSizeRange(JNIEnv * env,jobject thiz)363 android_media_visualizer_native_getCaptureSizeRange(JNIEnv *env, jobject thiz)
364 {
365 jintArray jRange = env->NewIntArray(2);
366 jint *nRange = env->GetIntArrayElements(jRange, NULL);
367 nRange[0] = Visualizer::getMinCaptureSize();
368 nRange[1] = Visualizer::getMaxCaptureSize();
369 LOGV("getCaptureSizeRange() min %d max %d", nRange[0], nRange[1]);
370 env->ReleaseIntArrayElements(jRange, nRange, 0);
371 return jRange;
372 }
373
374 static jint
android_media_visualizer_native_getMaxCaptureRate(JNIEnv * env,jobject thiz)375 android_media_visualizer_native_getMaxCaptureRate(JNIEnv *env, jobject thiz)
376 {
377 return Visualizer::getMaxCaptureRate();
378 }
379
380 static jint
android_media_visualizer_native_setCaptureSize(JNIEnv * env,jobject thiz,jint size)381 android_media_visualizer_native_setCaptureSize(JNIEnv *env, jobject thiz, jint size)
382 {
383 Visualizer* lpVisualizer = getVisualizer(env, thiz);
384 if (lpVisualizer == NULL) {
385 return VISUALIZER_ERROR_NO_INIT;
386 }
387
388 return translateError(lpVisualizer->setCaptureSize(size));
389 }
390
391 static jint
android_media_visualizer_native_getCaptureSize(JNIEnv * env,jobject thiz)392 android_media_visualizer_native_getCaptureSize(JNIEnv *env, jobject thiz)
393 {
394 Visualizer* lpVisualizer = getVisualizer(env, thiz);
395 if (lpVisualizer == NULL) {
396 return -1;
397 }
398 return lpVisualizer->getCaptureSize();
399 }
400
401 static jint
android_media_visualizer_native_getSamplingRate(JNIEnv * env,jobject thiz)402 android_media_visualizer_native_getSamplingRate(JNIEnv *env, jobject thiz)
403 {
404 Visualizer* lpVisualizer = getVisualizer(env, thiz);
405 if (lpVisualizer == NULL) {
406 return -1;
407 }
408 return lpVisualizer->getSamplingRate();
409 }
410
411 static jint
android_media_visualizer_native_getWaveForm(JNIEnv * env,jobject thiz,jbyteArray jWaveform)412 android_media_visualizer_native_getWaveForm(JNIEnv *env, jobject thiz, jbyteArray jWaveform)
413 {
414 Visualizer* lpVisualizer = getVisualizer(env, thiz);
415 if (lpVisualizer == NULL) {
416 return VISUALIZER_ERROR_NO_INIT;
417 }
418
419 jbyte* nWaveform = (jbyte *) env->GetPrimitiveArrayCritical(jWaveform, NULL);
420 if (nWaveform == NULL) {
421 return VISUALIZER_ERROR_NO_MEMORY;
422 }
423 jint status = translateError(lpVisualizer->getWaveForm((uint8_t *)nWaveform));
424
425 env->ReleasePrimitiveArrayCritical(jWaveform, nWaveform, 0);
426 return status;
427 }
428
429 static jint
android_media_visualizer_native_getFft(JNIEnv * env,jobject thiz,jbyteArray jFft)430 android_media_visualizer_native_getFft(JNIEnv *env, jobject thiz, jbyteArray jFft)
431 {
432 Visualizer* lpVisualizer = getVisualizer(env, thiz);
433 if (lpVisualizer == NULL) {
434 return VISUALIZER_ERROR_NO_INIT;
435 }
436
437 jbyte* nFft = (jbyte *) env->GetPrimitiveArrayCritical(jFft, NULL);
438 if (nFft == NULL) {
439 return VISUALIZER_ERROR_NO_MEMORY;
440 }
441 jint status = translateError(lpVisualizer->getFft((uint8_t *)nFft));
442
443 env->ReleasePrimitiveArrayCritical(jFft, nFft, 0);
444
445 return status;
446 }
447
448 static jint
android_media_setPeriodicCapture(JNIEnv * env,jobject thiz,jint rate,jboolean jWaveform,jboolean jFft)449 android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean jWaveform, jboolean jFft)
450 {
451 Visualizer* lpVisualizer = getVisualizer(env, thiz);
452 if (lpVisualizer == NULL) {
453 return VISUALIZER_ERROR_NO_INIT;
454 }
455 visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(thiz,
456 fields.fidJniData);
457 if (lpJniStorage == NULL) {
458 return VISUALIZER_ERROR_NO_INIT;
459 }
460
461 LOGV("setPeriodicCapture: rate %d, jWaveform %d jFft %d",
462 rate,
463 jWaveform,
464 jFft);
465
466 uint32_t flags = Visualizer::CAPTURE_CALL_JAVA;
467 if (jWaveform) flags |= Visualizer::CAPTURE_WAVEFORM;
468 if (jFft) flags |= Visualizer::CAPTURE_FFT;
469 Visualizer::capture_cbk_t cbk = captureCallback;
470 if (!jWaveform && !jFft) cbk = NULL;
471
472 return translateError(lpVisualizer->setCaptureCallBack(cbk,
473 &lpJniStorage->mCallbackData,
474 flags,
475 rate));
476 }
477
478 // ----------------------------------------------------------------------------
479
480 // Dalvik VM type signatures
481 static JNINativeMethod gMethods[] = {
482 {"native_init", "()V", (void *)android_media_visualizer_native_init},
483 {"native_setup", "(Ljava/lang/Object;I[I)I",
484 (void *)android_media_visualizer_native_setup},
485 {"native_finalize", "()V", (void *)android_media_visualizer_native_finalize},
486 {"native_release", "()V", (void *)android_media_visualizer_native_release},
487 {"native_setEnabled", "(Z)I", (void *)android_media_visualizer_native_setEnabled},
488 {"native_getEnabled", "()Z", (void *)android_media_visualizer_native_getEnabled},
489 {"getCaptureSizeRange", "()[I", (void *)android_media_visualizer_native_getCaptureSizeRange},
490 {"getMaxCaptureRate", "()I", (void *)android_media_visualizer_native_getMaxCaptureRate},
491 {"native_setCaptureSize", "(I)I", (void *)android_media_visualizer_native_setCaptureSize},
492 {"native_getCaptureSize", "()I", (void *)android_media_visualizer_native_getCaptureSize},
493 {"native_getSamplingRate", "()I", (void *)android_media_visualizer_native_getSamplingRate},
494 {"native_getWaveForm", "([B)I", (void *)android_media_visualizer_native_getWaveForm},
495 {"native_getFft", "([B)I", (void *)android_media_visualizer_native_getFft},
496 {"native_setPeriodicCapture","(IZZ)I",(void *)android_media_setPeriodicCapture},
497 };
498
499 // ----------------------------------------------------------------------------
500
register_android_media_visualizer(JNIEnv * env)501 int register_android_media_visualizer(JNIEnv *env)
502 {
503 return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
504 }
505
506