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
31using 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// ----------------------------------------------------------------------------
47static const char* const kClassPathName = "android/media/audiofx/Visualizer";
48static const char* const kClassPeakRmsPathName =
49        "android/media/audiofx/Visualizer$MeasurementPeakRms";
50
51struct 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};
60static fields_t fields;
61
62struct 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
77    visualizer_callback_cookie() {
78        waveform_data = NULL;
79        fft_data = NULL;
80    }
81
82    ~visualizer_callback_cookie() {
83        cleanupBuffers();
84    }
85
86    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// ----------------------------------------------------------------------------
105class VisualizerJniStorage {
106    public:
107        visualizer_callback_cookie mCallbackData;
108
109    VisualizerJniStorage() {
110    }
111
112    ~VisualizerJniStorage() {
113    }
114};
115
116
117static 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
138static Mutex sLock;
139
140// ----------------------------------------------------------------------------
141static 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
161static 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
233static 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
241static 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.
261static void
262android_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
332static 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
350static jint
351android_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:
420setup_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// ----------------------------------------------------------------------------
437static 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
459static 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// ----------------------------------------------------------------------------
465static jint
466android_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
486static jboolean
487android_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
501static jintArray
502android_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
513static jint
514android_media_visualizer_native_getMaxCaptureRate(JNIEnv* /* env */, jobject /* thiz */)
515{
516    return (jint) Visualizer::getMaxCaptureRate();
517}
518
519static jint
520android_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
530static jint
531android_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
540static jint
541android_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
551static jint
552android_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
561static jint
562android_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
571static jint
572android_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
581static jint
582android_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
591static jint
592android_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
609static jint
610android_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
628static jint
629android_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
647static jint
648android_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
680static 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
706int register_android_media_visualizer(JNIEnv *env)
707{
708    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
709}
710
711