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