android_media_Visualizer.cpp revision 06ade6ae1bd015e8b8ad0685847911213c93cc5b
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/audiofx/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    ALOGV("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
162static 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.
177static void
178android_media_visualizer_native_init(JNIEnv *env)
179{
180
181    ALOGV("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
224static jint
225android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
226        jint sessionId, jintArray jId)
227{
228    ALOGV("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    ALOGV("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:
289setup_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// ----------------------------------------------------------------------------
309static void android_media_visualizer_native_finalize(JNIEnv *env,  jobject thiz) {
310    ALOGV("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        ALOGV("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        ALOGV("deleting pJniStorage: %x\n", (int)lpJniStorage);
325        delete lpJniStorage;
326    }
327}
328
329// ----------------------------------------------------------------------------
330static 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
340static jint
341android_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
351static jboolean
352android_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
362static jintArray
363android_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    ALOGV("getCaptureSizeRange() min %d max %d", nRange[0], nRange[1]);
370    env->ReleaseIntArrayElements(jRange, nRange, 0);
371    return jRange;
372}
373
374static jint
375android_media_visualizer_native_getMaxCaptureRate(JNIEnv *env, jobject thiz)
376{
377    return Visualizer::getMaxCaptureRate();
378}
379
380static jint
381android_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
391static jint
392android_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
401static jint
402android_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
411static jint
412android_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
429static jint
430android_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
448static jint
449android_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    ALOGV("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
481static 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
501int register_android_media_visualizer(JNIEnv *env)
502{
503    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
504}
505
506