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