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