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