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