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