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