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 <utils/threads.h>
27 #include "media/Visualizer.h"
28 
29 #include <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                 0,
200                 jArray);
201         }
202     }
203 
204     if (fftSize != 0 && fft != NULL) {
205         jbyteArray jArray;
206 
207         ensureArraySize(env, &callbackInfo->fft_data, fftSize);
208         jArray = callbackInfo->fft_data;
209 
210         if (jArray != NULL) {
211             jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
212             memcpy(nArray, fft, fftSize);
213             env->ReleaseByteArrayElements(jArray, nArray, 0);
214             env->CallStaticVoidMethod(
215                 callbackInfo->visualizer_class,
216                 fields.midPostNativeEvent,
217                 callbackInfo->visualizer_ref,
218                 NATIVE_EVENT_FFT_CAPTURE,
219                 samplingrate,
220                 0,
221                 jArray);
222         }
223     }
224 
225     if (env->ExceptionCheck()) {
226         env->ExceptionDescribe();
227         env->ExceptionClear();
228     }
229 }
230 
231 // ----------------------------------------------------------------------------
232 
getVisualizer(JNIEnv * env,jobject thiz)233 static sp<Visualizer> getVisualizer(JNIEnv* env, jobject thiz)
234 {
235     Mutex::Autolock l(sLock);
236     Visualizer* const v =
237             (Visualizer*)env->GetLongField(thiz, fields.fidNativeVisualizer);
238     return sp<Visualizer>(v);
239 }
240 
setVisualizer(JNIEnv * env,jobject thiz,const sp<Visualizer> & v)241 static sp<Visualizer> setVisualizer(JNIEnv* env, jobject thiz,
242                                     const sp<Visualizer>& v)
243 {
244     Mutex::Autolock l(sLock);
245     sp<Visualizer> old =
246             (Visualizer*)env->GetLongField(thiz, fields.fidNativeVisualizer);
247     if (v.get()) {
248         v->incStrong((void*)setVisualizer);
249     }
250     if (old != 0) {
251         old->decStrong((void*)setVisualizer);
252     }
253     env->SetLongField(thiz, fields.fidNativeVisualizer, (jlong)v.get());
254     return old;
255 }
256 
257 // ----------------------------------------------------------------------------
258 // This function gets some field IDs, which in turn causes class initialization.
259 // It is called from a static block in Visualizer, which won't run until the
260 // first time an instance of this class is used.
261 static void
android_media_visualizer_native_init(JNIEnv * env)262 android_media_visualizer_native_init(JNIEnv *env)
263 {
264 
265     ALOGV("android_media_visualizer_native_init");
266 
267     fields.clazzEffect = NULL;
268 
269     // Get the Visualizer class
270     jclass clazz = env->FindClass(kClassPathName);
271     if (clazz == NULL) {
272         ALOGE("Can't find %s", kClassPathName);
273         return;
274     }
275 
276     fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);
277 
278     // Get the Visualizer.MeasurementPeakRms class
279     clazz = env->FindClass(kClassPeakRmsPathName);
280     if (clazz == NULL) {
281         ALOGE("Can't find %s", kClassPeakRmsPathName);
282         return;
283     }
284     jclass clazzMeasurementPeakRms = (jclass)env->NewGlobalRef(clazz);
285 
286     // Get the postEvent method
287     fields.midPostNativeEvent = env->GetStaticMethodID(
288             fields.clazzEffect,
289             "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
290     if (fields.midPostNativeEvent == NULL) {
291         ALOGE("Can't find Visualizer.%s", "postEventFromNative");
292         return;
293     }
294 
295     // Get the variables fields
296     //      nativeTrackInJavaObj
297     fields.fidNativeVisualizer = env->GetFieldID(
298             fields.clazzEffect,
299             "mNativeVisualizer", "J");
300     if (fields.fidNativeVisualizer == NULL) {
301         ALOGE("Can't find Visualizer.%s", "mNativeVisualizer");
302         return;
303     }
304     //      fidJniData;
305     fields.fidJniData = env->GetFieldID(
306             fields.clazzEffect,
307             "mJniData", "J");
308     if (fields.fidJniData == NULL) {
309         ALOGE("Can't find Visualizer.%s", "mJniData");
310         return;
311     }
312     //      fidPeak
313     fields.fidPeak = env->GetFieldID(
314             clazzMeasurementPeakRms,
315             "mPeak", "I");
316     if (fields.fidPeak == NULL) {
317         ALOGE("Can't find Visualizer.MeasurementPeakRms.%s", "mPeak");
318         return;
319     }
320     //      fidRms
321     fields.fidRms = env->GetFieldID(
322             clazzMeasurementPeakRms,
323             "mRms", "I");
324     if (fields.fidRms == NULL) {
325         ALOGE("Can't find Visualizer.MeasurementPeakRms.%s", "mPeak");
326         return;
327     }
328 
329     env->DeleteGlobalRef(clazzMeasurementPeakRms);
330 }
331 
android_media_visualizer_effect_callback(int32_t event,void * user,void * info)332 static void android_media_visualizer_effect_callback(int32_t event,
333                                                      void *user,
334                                                      void *info) {
335     if ((event == AudioEffect::EVENT_ERROR) &&
336         (*((status_t*)info) == DEAD_OBJECT)) {
337         VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage*)user;
338         visualizer_callback_cookie* callbackInfo = &lpJniStorage->mCallbackData;
339         JNIEnv *env = AndroidRuntime::getJNIEnv();
340 
341         env->CallStaticVoidMethod(
342             callbackInfo->visualizer_class,
343             fields.midPostNativeEvent,
344             callbackInfo->visualizer_ref,
345             NATIVE_EVENT_SERVER_DIED,
346             0, 0, NULL);
347     }
348 }
349 
350 static jint
android_media_visualizer_native_setup(JNIEnv * env,jobject thiz,jobject weak_this,jint sessionId,jintArray jId,jstring opPackageName)351 android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
352         jint sessionId, jintArray jId, jstring opPackageName)
353 {
354     ALOGV("android_media_visualizer_native_setup");
355     VisualizerJniStorage* lpJniStorage = NULL;
356     int lStatus = VISUALIZER_ERROR_NO_MEMORY;
357     sp<Visualizer> lpVisualizer;
358     jint* nId = NULL;
359 
360     ScopedUtfChars opPackageNameStr(env, opPackageName);
361 
362     setVisualizer(env, thiz, 0);
363 
364     lpJniStorage = new VisualizerJniStorage();
365     if (lpJniStorage == NULL) {
366         ALOGE("setup: Error creating JNI Storage");
367         goto setup_failure;
368     }
369 
370     lpJniStorage->mCallbackData.visualizer_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
371     // we use a weak reference so the Visualizer object can be garbage collected.
372     lpJniStorage->mCallbackData.visualizer_ref = env->NewGlobalRef(weak_this);
373 
374     ALOGV("setup: lpJniStorage: %p visualizer_ref %p visualizer_class %p, &mCallbackData %p",
375             lpJniStorage,
376             lpJniStorage->mCallbackData.visualizer_ref,
377             lpJniStorage->mCallbackData.visualizer_class,
378             &lpJniStorage->mCallbackData);
379 
380     if (jId == NULL) {
381         ALOGE("setup: NULL java array for id pointer");
382         lStatus = VISUALIZER_ERROR_BAD_VALUE;
383         goto setup_failure;
384     }
385 
386     // create the native Visualizer object
387     lpVisualizer = new Visualizer(String16(opPackageNameStr.c_str()),
388                                   0,
389                                   android_media_visualizer_effect_callback,
390                                   lpJniStorage,
391                                   (audio_session_t) sessionId);
392     if (lpVisualizer == 0) {
393         ALOGE("Error creating Visualizer");
394         goto setup_failure;
395     }
396 
397     lStatus = translateError(lpVisualizer->initCheck());
398     if (lStatus != VISUALIZER_SUCCESS && lStatus != VISUALIZER_ERROR_ALREADY_EXISTS) {
399         ALOGE("Visualizer initCheck failed %d", lStatus);
400         goto setup_failure;
401     }
402 
403     nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
404     if (nId == NULL) {
405         ALOGE("setup: Error retrieving id pointer");
406         lStatus = VISUALIZER_ERROR_BAD_VALUE;
407         goto setup_failure;
408     }
409     nId[0] = lpVisualizer->id();
410     env->ReleasePrimitiveArrayCritical(jId, nId, 0);
411     nId = NULL;
412 
413     setVisualizer(env, thiz, lpVisualizer);
414 
415     env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
416 
417     return VISUALIZER_SUCCESS;
418 
419     // failures:
420 setup_failure:
421 
422     if (nId != NULL) {
423         env->ReleasePrimitiveArrayCritical(jId, nId, 0);
424     }
425 
426     if (lpJniStorage) {
427         env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_class);
428         env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_ref);
429         delete lpJniStorage;
430     }
431     env->SetLongField(thiz, fields.fidJniData, 0);
432 
433     return (jint) lStatus;
434 }
435 
436 // ----------------------------------------------------------------------------
android_media_visualizer_native_release(JNIEnv * env,jobject thiz)437 static void android_media_visualizer_native_release(JNIEnv *env,  jobject thiz) {
438     sp<Visualizer> lpVisualizer = setVisualizer(env, thiz, 0);
439     if (lpVisualizer == 0) {
440         return;
441     }
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