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