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