• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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