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 <jni.h>
24 #include <nativehelper/JNIHelp.h>
25 #include <android_runtime/AndroidRuntime.h>
26 #include <utils/threads.h>
27 #include "Visualizer.h"
28 
29 #include <nativehelper/ScopedUtfChars.h>
30 
31 using namespace android;
32 
33 #define VISUALIZER_SUCCESS                      0
34 #define VISUALIZER_ERROR                       (-1)
35 #define VISUALIZER_ERROR_ALREADY_EXISTS        (-2)
36 #define VISUALIZER_ERROR_NO_INIT               (-3)
37 #define VISUALIZER_ERROR_BAD_VALUE             (-4)
38 #define VISUALIZER_ERROR_INVALID_OPERATION     (-5)
39 #define VISUALIZER_ERROR_NO_MEMORY             (-6)
40 #define VISUALIZER_ERROR_DEAD_OBJECT           (-7)
41 
42 #define NATIVE_EVENT_PCM_CAPTURE                0
43 #define NATIVE_EVENT_FFT_CAPTURE                1
44 #define NATIVE_EVENT_SERVER_DIED                2
45 
46 // ----------------------------------------------------------------------------
47 static const char* const kClassPathName = "android/media/audiofx/Visualizer";
48 static const char* const kClassPeakRmsPathName =
49         "android/media/audiofx/Visualizer$MeasurementPeakRms";
50 
51 struct fields_t {
52     // these fields provide access from C++ to the...
53     jclass    clazzEffect;          // Visualizer class
54     jmethodID midPostNativeEvent;   // event post callback method
55     jfieldID  fidNativeVisualizer; // stores in Java the native Visualizer object
56     jfieldID  fidJniData;           // stores in Java additional resources used by the native Visualizer
57     jfieldID  fidPeak; // to access Visualizer.MeasurementPeakRms.mPeak
58     jfieldID  fidRms;  // to access Visualizer.MeasurementPeakRms.mRms
59 };
60 static fields_t fields;
61 
62 struct visualizer_callback_cookie {
63     jclass      visualizer_class;  // Visualizer class
64     jobject     visualizer_ref;    // Visualizer object instance
65 
66     // Lazily allocated arrays used to hold callback data provided to java
67     // applications.  These arrays are allocated during the first callback and
68     // reallocated when the size of the callback data changes.  Allocating on
69     // demand and saving the arrays means that applications cannot safely hold a
70     // reference to the provided data (they need to make a copy if they want to
71     // hold onto outside of the callback scope), but it avoids GC thrash caused
72     // by constantly allocating and releasing arrays to hold callback data.
73     Mutex       callback_data_lock;
74     jbyteArray  waveform_data;
75     jbyteArray  fft_data;
76 
visualizer_callback_cookievisualizer_callback_cookie77     visualizer_callback_cookie() {
78         waveform_data = NULL;
79         fft_data = NULL;
80     }
81 
~visualizer_callback_cookievisualizer_callback_cookie82     ~visualizer_callback_cookie() {
83         cleanupBuffers();
84     }
85 
cleanupBuffersvisualizer_callback_cookie86     void cleanupBuffers() {
87         AutoMutex lock(&callback_data_lock);
88         if (waveform_data || fft_data) {
89             JNIEnv *env = AndroidRuntime::getJNIEnv();
90 
91             if (waveform_data) {
92                 env->DeleteGlobalRef(waveform_data);
93                 waveform_data = NULL;
94             }
95 
96             if (fft_data) {
97                 env->DeleteGlobalRef(fft_data);
98                 fft_data = NULL;
99             }
100         }
101     }
102  };
103 
104 // ----------------------------------------------------------------------------
105 class VisualizerJniStorage {
106     public:
107         visualizer_callback_cookie mCallbackData;
108 
VisualizerJniStorage()109     VisualizerJniStorage() {
110     }
111 
~VisualizerJniStorage()112     ~VisualizerJniStorage() {
113     }
114 };
115 
116 
translateError(int code)117 static jint translateError(int code) {
118     switch(code) {
119     case NO_ERROR:
120         return VISUALIZER_SUCCESS;
121     case ALREADY_EXISTS:
122         return VISUALIZER_ERROR_ALREADY_EXISTS;
123     case NO_INIT:
124         return VISUALIZER_ERROR_NO_INIT;
125     case BAD_VALUE:
126         return VISUALIZER_ERROR_BAD_VALUE;
127     case INVALID_OPERATION:
128         return VISUALIZER_ERROR_INVALID_OPERATION;
129     case NO_MEMORY:
130         return VISUALIZER_ERROR_NO_MEMORY;
131     case DEAD_OBJECT:
132         return VISUALIZER_ERROR_DEAD_OBJECT;
133     default:
134         return VISUALIZER_ERROR;
135     }
136 }
137 
138 static Mutex sLock;
139 
140 // ----------------------------------------------------------------------------
ensureArraySize(JNIEnv * env,jbyteArray * array,uint32_t size)141 static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) {
142     if (NULL != *array) {
143         uint32_t len = env->GetArrayLength(*array);
144         if (len == size)
145             return;
146 
147         env->DeleteGlobalRef(*array);
148         *array = NULL;
149     }
150 
151     jbyteArray localRef = env->NewByteArray(size);
152     if (NULL != localRef) {
153         // Promote to global ref.
154         *array = (jbyteArray)env->NewGlobalRef(localRef);
155 
156         // Release our (now pointless) local ref.
157         env->DeleteLocalRef(localRef);
158     }
159 }
160 
captureCallback(void * user,uint32_t waveformSize,uint8_t * waveform,uint32_t fftSize,uint8_t * fft,uint32_t samplingrate)161 static void captureCallback(void* user,
162         uint32_t waveformSize,
163         uint8_t *waveform,
164         uint32_t fftSize,
165         uint8_t *fft,
166         uint32_t samplingrate) {
167 
168     visualizer_callback_cookie *callbackInfo = (visualizer_callback_cookie *)user;
169     JNIEnv *env = AndroidRuntime::getJNIEnv();
170 
171     if (!user || !env) {
172         ALOGW("captureCallback error user %p, env %p", user, env);
173         return;
174     }
175 
176     ALOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
177             callbackInfo,
178             callbackInfo->visualizer_ref,
179             callbackInfo->visualizer_class);
180 
181     AutoMutex lock(&callbackInfo->callback_data_lock);
182 
183     if (waveformSize != 0 && waveform != NULL) {
184         jbyteArray jArray;
185 
186         ensureArraySize(env, &callbackInfo->waveform_data, waveformSize);
187         jArray = callbackInfo->waveform_data;
188 
189         if (jArray != NULL) {
190             jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
191             memcpy(nArray, waveform, waveformSize);
192             env->ReleaseByteArrayElements(jArray, nArray, 0);
193             env->CallStaticVoidMethod(
194                 callbackInfo->visualizer_class,
195                 fields.midPostNativeEvent,
196                 callbackInfo->visualizer_ref,
197                 NATIVE_EVENT_PCM_CAPTURE,
198                 samplingrate,
199                 jArray);
200         }
201     }
202 
203     if (fftSize != 0 && fft != NULL) {
204         jbyteArray jArray;
205 
206         ensureArraySize(env, &callbackInfo->fft_data, fftSize);
207         jArray = callbackInfo->fft_data;
208 
209         if (jArray != NULL) {
210             jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
211             memcpy(nArray, fft, fftSize);
212             env->ReleaseByteArrayElements(jArray, nArray, 0);
213             env->CallStaticVoidMethod(
214                 callbackInfo->visualizer_class,
215                 fields.midPostNativeEvent,
216                 callbackInfo->visualizer_ref,
217                 NATIVE_EVENT_FFT_CAPTURE,
218                 samplingrate,
219                 jArray);
220         }
221     }
222 
223     if (env->ExceptionCheck()) {
224         env->ExceptionDescribe();
225         env->ExceptionClear();
226     }
227 }
228 
229 // ----------------------------------------------------------------------------
230 
getVisualizer(JNIEnv * env,jobject thiz)231 static sp<Visualizer> getVisualizer(JNIEnv* env, jobject thiz)
232 {
233     Mutex::Autolock l(sLock);
234     Visualizer* const v =
235             (Visualizer*)env->GetLongField(thiz, fields.fidNativeVisualizer);
236     return sp<Visualizer>(v);
237 }
238 
setVisualizer(JNIEnv * env,jobject thiz,const sp<Visualizer> & v)239 static sp<Visualizer> setVisualizer(JNIEnv* env, jobject thiz,
240                                     const sp<Visualizer>& v)
241 {
242     Mutex::Autolock l(sLock);
243     sp<Visualizer> old =
244             (Visualizer*)env->GetLongField(thiz, fields.fidNativeVisualizer);
245     if (v.get()) {
246         v->incStrong((void*)setVisualizer);
247     }
248     if (old != 0) {
249         old->decStrong((void*)setVisualizer);
250     }
251     env->SetLongField(thiz, fields.fidNativeVisualizer, (jlong)v.get());
252     return old;
253 }
254 
255 // ----------------------------------------------------------------------------
256 // This function gets some field IDs, which in turn causes class initialization.
257 // It is called from a static block in Visualizer, which won't run until the
258 // first time an instance of this class is used.
259 static void
android_media_visualizer_native_init(JNIEnv * env)260 android_media_visualizer_native_init(JNIEnv *env)
261 {
262 
263     ALOGV("android_media_visualizer_native_init");
264 
265     fields.clazzEffect = NULL;
266 
267     // Get the Visualizer class
268     jclass clazz = env->FindClass(kClassPathName);
269     if (clazz == NULL) {
270         ALOGE("Can't find %s", kClassPathName);
271         return;
272     }
273 
274     fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);
275 
276     // Get the Visualizer.MeasurementPeakRms class
277     clazz = env->FindClass(kClassPeakRmsPathName);
278     if (clazz == NULL) {
279         ALOGE("Can't find %s", kClassPeakRmsPathName);
280         return;
281     }
282     jclass clazzMeasurementPeakRms = (jclass)env->NewGlobalRef(clazz);
283 
284     // Get the postEvent method
285     fields.midPostNativeEvent = env->GetStaticMethodID(
286             fields.clazzEffect,
287             "postEventFromNative", "(Ljava/lang/Object;II[B)V");
288     if (fields.midPostNativeEvent == NULL) {
289         ALOGE("Can't find Visualizer.%s", "postEventFromNative");
290         return;
291     }
292 
293     // Get the variables fields
294     //      nativeTrackInJavaObj
295     fields.fidNativeVisualizer = env->GetFieldID(
296             fields.clazzEffect,
297             "mNativeVisualizer", "J");
298     if (fields.fidNativeVisualizer == NULL) {
299         ALOGE("Can't find Visualizer.%s", "mNativeVisualizer");
300         return;
301     }
302     //      fidJniData;
303     fields.fidJniData = env->GetFieldID(
304             fields.clazzEffect,
305             "mJniData", "J");
306     if (fields.fidJniData == NULL) {
307         ALOGE("Can't find Visualizer.%s", "mJniData");
308         return;
309     }
310     //      fidPeak
311     fields.fidPeak = env->GetFieldID(
312             clazzMeasurementPeakRms,
313             "mPeak", "I");
314     if (fields.fidPeak == NULL) {
315         ALOGE("Can't find Visualizer.MeasurementPeakRms.%s", "mPeak");
316         return;
317     }
318     //      fidRms
319     fields.fidRms = env->GetFieldID(
320             clazzMeasurementPeakRms,
321             "mRms", "I");
322     if (fields.fidRms == NULL) {
323         ALOGE("Can't find Visualizer.MeasurementPeakRms.%s", "mPeak");
324         return;
325     }
326 
327     env->DeleteGlobalRef(clazzMeasurementPeakRms);
328 }
329 
android_media_visualizer_effect_callback(int32_t event,void * user,void * info)330 static void android_media_visualizer_effect_callback(int32_t event,
331                                                      void *user,
332                                                      void *info) {
333     if ((event == AudioEffect::EVENT_ERROR) &&
334         (*((status_t*)info) == DEAD_OBJECT)) {
335         VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage*)user;
336         visualizer_callback_cookie* callbackInfo = &lpJniStorage->mCallbackData;
337         JNIEnv *env = AndroidRuntime::getJNIEnv();
338 
339         env->CallStaticVoidMethod(
340             callbackInfo->visualizer_class,
341             fields.midPostNativeEvent,
342             callbackInfo->visualizer_ref,
343             NATIVE_EVENT_SERVER_DIED,
344             0, NULL);
345     }
346 }
347 
348 static jint
android_media_visualizer_native_setup(JNIEnv * env,jobject thiz,jobject weak_this,jint sessionId,jintArray jId,jstring opPackageName)349 android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
350         jint sessionId, jintArray jId, jstring opPackageName)
351 {
352     ALOGV("android_media_visualizer_native_setup");
353     VisualizerJniStorage* lpJniStorage = NULL;
354     int lStatus = VISUALIZER_ERROR_NO_MEMORY;
355     sp<Visualizer> lpVisualizer;
356     jint* nId = NULL;
357 
358     ScopedUtfChars opPackageNameStr(env, opPackageName);
359 
360     setVisualizer(env, thiz, 0);
361 
362     lpJniStorage = new VisualizerJniStorage();
363     if (lpJniStorage == NULL) {
364         ALOGE("setup: Error creating JNI Storage");
365         goto setup_failure;
366     }
367 
368     lpJniStorage->mCallbackData.visualizer_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
369     // we use a weak reference so the Visualizer object can be garbage collected.
370     lpJniStorage->mCallbackData.visualizer_ref = env->NewGlobalRef(weak_this);
371 
372     ALOGV("setup: lpJniStorage: %p visualizer_ref %p visualizer_class %p, &mCallbackData %p",
373             lpJniStorage,
374             lpJniStorage->mCallbackData.visualizer_ref,
375             lpJniStorage->mCallbackData.visualizer_class,
376             &lpJniStorage->mCallbackData);
377 
378     if (jId == NULL) {
379         ALOGE("setup: NULL java array for id pointer");
380         lStatus = VISUALIZER_ERROR_BAD_VALUE;
381         goto setup_failure;
382     }
383 
384     // create the native Visualizer object
385     lpVisualizer = new Visualizer(String16(opPackageNameStr.c_str()),
386                                   0,
387                                   android_media_visualizer_effect_callback,
388                                   lpJniStorage,
389                                   (audio_session_t) sessionId);
390     if (lpVisualizer == 0) {
391         ALOGE("Error creating Visualizer");
392         goto setup_failure;
393     }
394 
395     lStatus = translateError(lpVisualizer->initCheck());
396     if (lStatus != VISUALIZER_SUCCESS && lStatus != VISUALIZER_ERROR_ALREADY_EXISTS) {
397         ALOGE("Visualizer initCheck failed %d", lStatus);
398         goto setup_failure;
399     }
400 
401     nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
402     if (nId == NULL) {
403         ALOGE("setup: Error retrieving id pointer");
404         lStatus = VISUALIZER_ERROR_BAD_VALUE;
405         goto setup_failure;
406     }
407     nId[0] = lpVisualizer->id();
408     env->ReleasePrimitiveArrayCritical(jId, nId, 0);
409     nId = NULL;
410 
411     setVisualizer(env, thiz, lpVisualizer);
412 
413     env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
414 
415     return VISUALIZER_SUCCESS;
416 
417     // failures:
418 setup_failure:
419 
420     if (nId != NULL) {
421         env->ReleasePrimitiveArrayCritical(jId, nId, 0);
422     }
423 
424     if (lpJniStorage) {
425         env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_class);
426         env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_ref);
427         delete lpJniStorage;
428     }
429     env->SetLongField(thiz, fields.fidJniData, 0);
430 
431     return (jint) lStatus;
432 }
433 
434 // ----------------------------------------------------------------------------
android_media_visualizer_native_release(JNIEnv * env,jobject thiz)435 static void android_media_visualizer_native_release(JNIEnv *env,  jobject thiz) {
436     { //limit scope so that lpVisualizer is deleted before JNI storage data.
437         sp<Visualizer> lpVisualizer = setVisualizer(env, thiz, 0);
438         if (lpVisualizer == 0) {
439             return;
440         }
441         lpVisualizer->release();
442     }
443     // delete the JNI data
444     VisualizerJniStorage* lpJniStorage =
445         (VisualizerJniStorage *)env->GetLongField(thiz, fields.fidJniData);
446 
447     // reset the native resources in the Java object so any attempt to access
448     // them after a call to release fails.
449     env->SetLongField(thiz, fields.fidJniData, 0);
450 
451     if (lpJniStorage) {
452         ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
453         env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_class);
454         env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_ref);
455         delete lpJniStorage;
456     }
457 }
458 
android_media_visualizer_native_finalize(JNIEnv * env,jobject thiz)459 static void android_media_visualizer_native_finalize(JNIEnv *env,  jobject thiz) {
460     ALOGV("android_media_visualizer_native_finalize jobject: %p\n", thiz);
461     android_media_visualizer_native_release(env, thiz);
462 }
463 
464 // ----------------------------------------------------------------------------
465 static jint
android_media_visualizer_native_setEnabled(JNIEnv * env,jobject thiz,jboolean enabled)466 android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
467 {
468     sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
469     if (lpVisualizer == 0) {
470         return VISUALIZER_ERROR_NO_INIT;
471     }
472 
473     jint retVal = translateError(lpVisualizer->setEnabled(enabled));
474 
475     if (!enabled) {
476         VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage *)env->GetLongField(
477             thiz, fields.fidJniData);
478 
479         if (NULL != lpJniStorage)
480             lpJniStorage->mCallbackData.cleanupBuffers();
481     }
482 
483     return retVal;
484 }
485 
486 static jboolean
android_media_visualizer_native_getEnabled(JNIEnv * env,jobject thiz)487 android_media_visualizer_native_getEnabled(JNIEnv *env, jobject thiz)
488 {
489     sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
490     if (lpVisualizer == 0) {
491         return JNI_FALSE;
492     }
493 
494     if (lpVisualizer->getEnabled()) {
495         return JNI_TRUE;
496     } else {
497         return JNI_FALSE;
498     }
499 }
500 
501 static jintArray
android_media_visualizer_native_getCaptureSizeRange(JNIEnv * env,jobject)502 android_media_visualizer_native_getCaptureSizeRange(JNIEnv *env, jobject /* thiz */)
503 {
504     jintArray jRange = env->NewIntArray(2);
505     jint *nRange = env->GetIntArrayElements(jRange, NULL);
506     nRange[0] = Visualizer::getMinCaptureSize();
507     nRange[1] = Visualizer::getMaxCaptureSize();
508     ALOGV("getCaptureSizeRange() min %d max %d", nRange[0], nRange[1]);
509     env->ReleaseIntArrayElements(jRange, nRange, 0);
510     return jRange;
511 }
512 
513 static jint
android_media_visualizer_native_getMaxCaptureRate(JNIEnv *,jobject)514 android_media_visualizer_native_getMaxCaptureRate(JNIEnv* /* env */, jobject /* thiz */)
515 {
516     return (jint) Visualizer::getMaxCaptureRate();
517 }
518 
519 static jint
android_media_visualizer_native_setCaptureSize(JNIEnv * env,jobject thiz,jint size)520 android_media_visualizer_native_setCaptureSize(JNIEnv *env, jobject thiz, jint size)
521 {
522     sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
523     if (lpVisualizer == 0) {
524         return VISUALIZER_ERROR_NO_INIT;
525     }
526 
527     return translateError(lpVisualizer->setCaptureSize(size));
528 }
529 
530 static jint
android_media_visualizer_native_getCaptureSize(JNIEnv * env,jobject thiz)531 android_media_visualizer_native_getCaptureSize(JNIEnv *env, jobject thiz)
532 {
533     sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
534     if (lpVisualizer == 0) {
535         return -1;
536     }
537     return (jint) lpVisualizer->getCaptureSize();
538 }
539 
540 static jint
android_media_visualizer_native_setScalingMode(JNIEnv * env,jobject thiz,jint mode)541 android_media_visualizer_native_setScalingMode(JNIEnv *env, jobject thiz, jint mode)
542 {
543     sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
544     if (lpVisualizer == 0) {
545         return VISUALIZER_ERROR_NO_INIT;
546     }
547 
548     return translateError(lpVisualizer->setScalingMode(mode));
549 }
550 
551 static jint
android_media_visualizer_native_getScalingMode(JNIEnv * env,jobject thiz)552 android_media_visualizer_native_getScalingMode(JNIEnv *env, jobject thiz)
553 {
554     sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
555     if (lpVisualizer == 0) {
556         return -1;
557     }
558     return (jint)lpVisualizer->getScalingMode();
559 }
560 
561 static jint
android_media_visualizer_native_setMeasurementMode(JNIEnv * env,jobject thiz,jint mode)562 android_media_visualizer_native_setMeasurementMode(JNIEnv *env, jobject thiz, jint mode)
563 {
564     sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
565     if (lpVisualizer == 0) {
566         return VISUALIZER_ERROR_NO_INIT;
567     }
568     return translateError(lpVisualizer->setMeasurementMode(mode));
569 }
570 
571 static jint
android_media_visualizer_native_getMeasurementMode(JNIEnv * env,jobject thiz)572 android_media_visualizer_native_getMeasurementMode(JNIEnv *env, jobject thiz)
573 {
574     sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
575     if (lpVisualizer == 0) {
576         return MEASUREMENT_MODE_NONE;
577     }
578     return lpVisualizer->getMeasurementMode();
579 }
580 
581 static jint
android_media_visualizer_native_getSamplingRate(JNIEnv * env,jobject thiz)582 android_media_visualizer_native_getSamplingRate(JNIEnv *env, jobject thiz)
583 {
584     sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
585     if (lpVisualizer == 0) {
586         return -1;
587     }
588     return (jint) lpVisualizer->getSamplingRate();
589 }
590 
591 static jint
android_media_visualizer_native_getWaveForm(JNIEnv * env,jobject thiz,jbyteArray jWaveform)592 android_media_visualizer_native_getWaveForm(JNIEnv *env, jobject thiz, jbyteArray jWaveform)
593 {
594     sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
595     if (lpVisualizer == 0) {
596         return VISUALIZER_ERROR_NO_INIT;
597     }
598 
599     jbyte* nWaveform = (jbyte *) env->GetPrimitiveArrayCritical(jWaveform, NULL);
600     if (nWaveform == NULL) {
601         return VISUALIZER_ERROR_NO_MEMORY;
602     }
603     jint status = translateError(lpVisualizer->getWaveForm((uint8_t *)nWaveform));
604 
605     env->ReleasePrimitiveArrayCritical(jWaveform, nWaveform, 0);
606     return status;
607 }
608 
609 static jint
android_media_visualizer_native_getFft(JNIEnv * env,jobject thiz,jbyteArray jFft)610 android_media_visualizer_native_getFft(JNIEnv *env, jobject thiz, jbyteArray jFft)
611 {
612     sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
613     if (lpVisualizer == 0) {
614         return VISUALIZER_ERROR_NO_INIT;
615     }
616 
617     jbyte* nFft = (jbyte *) env->GetPrimitiveArrayCritical(jFft, NULL);
618     if (nFft == NULL) {
619         return VISUALIZER_ERROR_NO_MEMORY;
620     }
621     jint status = translateError(lpVisualizer->getFft((uint8_t *)nFft));
622 
623     env->ReleasePrimitiveArrayCritical(jFft, nFft, 0);
624 
625     return status;
626 }
627 
628 static jint
android_media_visualizer_native_getPeakRms(JNIEnv * env,jobject thiz,jobject jPeakRmsObj)629 android_media_visualizer_native_getPeakRms(JNIEnv *env, jobject thiz, jobject jPeakRmsObj)
630 {
631     sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
632     if (lpVisualizer == 0) {
633         return VISUALIZER_ERROR_NO_INIT;
634     }
635     int32_t measurements[2];
636     jint status = translateError(
637                 lpVisualizer->getIntMeasurements(MEASUREMENT_MODE_PEAK_RMS,
638                         2, measurements));
639     if (status == VISUALIZER_SUCCESS) {
640         // measurement worked, write the values to the java object
641         env->SetIntField(jPeakRmsObj, fields.fidPeak, measurements[MEASUREMENT_IDX_PEAK]);
642         env->SetIntField(jPeakRmsObj, fields.fidRms, measurements[MEASUREMENT_IDX_RMS]);
643     }
644     return status;
645 }
646 
647 static jint
android_media_setPeriodicCapture(JNIEnv * env,jobject thiz,jint rate,jboolean jWaveform,jboolean jFft)648 android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean jWaveform, jboolean jFft)
649 {
650     sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
651     if (lpVisualizer == 0) {
652         return VISUALIZER_ERROR_NO_INIT;
653     }
654     VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage *)env->GetLongField(thiz,
655             fields.fidJniData);
656     if (lpJniStorage == NULL) {
657         return VISUALIZER_ERROR_NO_INIT;
658     }
659 
660     ALOGV("setPeriodicCapture: rate %d, jWaveform %d jFft %d",
661             rate,
662             jWaveform,
663             jFft);
664 
665     uint32_t flags = Visualizer::CAPTURE_CALL_JAVA;
666     if (jWaveform) flags |= Visualizer::CAPTURE_WAVEFORM;
667     if (jFft) flags |= Visualizer::CAPTURE_FFT;
668     Visualizer::capture_cbk_t cbk = captureCallback;
669     if (!jWaveform && !jFft) cbk = NULL;
670 
671     return translateError(lpVisualizer->setCaptureCallBack(cbk,
672                                                 &lpJniStorage->mCallbackData,
673                                                 flags,
674                                                 rate));
675 }
676 
677 // ----------------------------------------------------------------------------
678 
679 // Dalvik VM type signatures
680 static const JNINativeMethod gMethods[] = {
681     {"native_init",            "()V",     (void *)android_media_visualizer_native_init},
682     {"native_setup",           "(Ljava/lang/Object;I[ILjava/lang/String;)I",
683                                           (void *)android_media_visualizer_native_setup},
684     {"native_finalize",          "()V",   (void *)android_media_visualizer_native_finalize},
685     {"native_release",           "()V",   (void *)android_media_visualizer_native_release},
686     {"native_setEnabled",        "(Z)I",  (void *)android_media_visualizer_native_setEnabled},
687     {"native_getEnabled",        "()Z",   (void *)android_media_visualizer_native_getEnabled},
688     {"getCaptureSizeRange",      "()[I",  (void *)android_media_visualizer_native_getCaptureSizeRange},
689     {"getMaxCaptureRate",        "()I",   (void *)android_media_visualizer_native_getMaxCaptureRate},
690     {"native_setCaptureSize",    "(I)I",  (void *)android_media_visualizer_native_setCaptureSize},
691     {"native_getCaptureSize",    "()I",   (void *)android_media_visualizer_native_getCaptureSize},
692     {"native_setScalingMode",    "(I)I",  (void *)android_media_visualizer_native_setScalingMode},
693     {"native_getScalingMode",    "()I",   (void *)android_media_visualizer_native_getScalingMode},
694     {"native_setMeasurementMode","(I)I",  (void *)android_media_visualizer_native_setMeasurementMode},
695     {"native_getMeasurementMode","()I",   (void *)android_media_visualizer_native_getMeasurementMode},
696     {"native_getSamplingRate",   "()I",   (void *)android_media_visualizer_native_getSamplingRate},
697     {"native_getWaveForm",       "([B)I", (void *)android_media_visualizer_native_getWaveForm},
698     {"native_getFft",            "([B)I", (void *)android_media_visualizer_native_getFft},
699     {"native_getPeakRms",      "(Landroid/media/audiofx/Visualizer$MeasurementPeakRms;)I",
700                                           (void *)android_media_visualizer_native_getPeakRms},
701     {"native_setPeriodicCapture","(IZZ)I",(void *)android_media_setPeriodicCapture},
702 };
703 
704 // ----------------------------------------------------------------------------
705 
register_android_media_visualizer(JNIEnv * env)706 int register_android_media_visualizer(JNIEnv *env)
707 {
708     return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
709 }
710 
711