android_media_AudioEffect.cpp revision 4fc28d78cb925c2780abf02c6826e77f8f8a230a
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 "AudioEffects-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/AudioEffect.h"
27
28using namespace android;
29
30#define AUDIOEFFECT_SUCCESS                      0
31#define AUDIOEFFECT_ERROR                       -1
32#define AUDIOEFFECT_ERROR_ALREADY_EXISTS        -2
33#define AUDIOEFFECT_ERROR_NO_INIT               -3
34#define AUDIOEFFECT_ERROR_BAD_VALUE             -4
35#define AUDIOEFFECT_ERROR_INVALID_OPERATION     -5
36#define AUDIOEFFECT_ERROR_NO_MEMORY             -6
37#define AUDIOEFFECT_ERROR_DEAD_OBJECT           -7
38
39// ----------------------------------------------------------------------------
40static const char* const kClassPathName = "android/media/audiofx/AudioEffect";
41
42struct fields_t {
43    // these fields provide access from C++ to the...
44    jclass    clazzEffect;          // AudioEffect class
45    jmethodID midPostNativeEvent;   // event post callback method
46    jfieldID  fidNativeAudioEffect; // stores in Java the native AudioEffect object
47    jfieldID  fidJniData;           // stores in Java additional resources used by the native AudioEffect
48    jclass    clazzDesc;            // AudioEffect.Descriptor class
49    jmethodID midDescCstor;         // AudioEffect.Descriptor class constructor
50};
51static fields_t fields;
52
53struct effect_callback_cookie {
54    jclass      audioEffect_class;  // AudioEffect class
55    jobject     audioEffect_ref;    // AudioEffect object instance
56 };
57
58// ----------------------------------------------------------------------------
59class AudioEffectJniStorage {
60    public:
61        effect_callback_cookie mCallbackData;
62
63    AudioEffectJniStorage() {
64    }
65
66    ~AudioEffectJniStorage() {
67    }
68
69};
70
71
72static jint translateError(int code) {
73    switch(code) {
74    case NO_ERROR:
75        return AUDIOEFFECT_SUCCESS;
76    case ALREADY_EXISTS:
77        return AUDIOEFFECT_ERROR_ALREADY_EXISTS;
78    case NO_INIT:
79        return AUDIOEFFECT_ERROR_NO_INIT;
80    case BAD_VALUE:
81        return AUDIOEFFECT_ERROR_BAD_VALUE;
82    case INVALID_OPERATION:
83        return AUDIOEFFECT_ERROR_INVALID_OPERATION;
84    case NO_MEMORY:
85        return AUDIOEFFECT_ERROR_NO_MEMORY;
86    case DEAD_OBJECT:
87        return AUDIOEFFECT_ERROR_DEAD_OBJECT;
88    default:
89        return AUDIOEFFECT_ERROR;
90    }
91}
92
93
94// ----------------------------------------------------------------------------
95static void effectCallback(int event, void* user, void *info) {
96
97    effect_param_t *p;
98    int arg1 = 0;
99    int arg2 = 0;
100    jobject obj = NULL;
101    jbyteArray array = NULL;
102    jbyte *bytes;
103    bool param;
104    size_t size;
105
106    effect_callback_cookie *callbackInfo = (effect_callback_cookie *)user;
107    JNIEnv *env = AndroidRuntime::getJNIEnv();
108
109    ALOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p",
110            callbackInfo,
111            callbackInfo->audioEffect_ref,
112            callbackInfo->audioEffect_class);
113
114    if (!user || !env) {
115        ALOGW("effectCallback error user %p, env %p", user, env);
116        return;
117    }
118
119    switch (event) {
120    case AudioEffect::EVENT_CONTROL_STATUS_CHANGED:
121        if (info == 0) {
122            ALOGW("EVENT_CONTROL_STATUS_CHANGED info == NULL");
123            goto effectCallback_Exit;
124        }
125        param = *(bool *)info;
126        arg1 = (int)param;
127        ALOGV("EVENT_CONTROL_STATUS_CHANGED");
128        break;
129    case AudioEffect::EVENT_ENABLE_STATUS_CHANGED:
130        if (info == 0) {
131            ALOGW("EVENT_ENABLE_STATUS_CHANGED info == NULL");
132            goto effectCallback_Exit;
133        }
134        param = *(bool *)info;
135        arg1 = (int)param;
136        ALOGV("EVENT_ENABLE_STATUS_CHANGED");
137        break;
138    case AudioEffect::EVENT_PARAMETER_CHANGED:
139        if (info == 0) {
140            ALOGW("EVENT_PARAMETER_CHANGED info == NULL");
141            goto effectCallback_Exit;
142        }
143        p = (effect_param_t *)info;
144        if (p->psize == 0 || p->vsize == 0) {
145            goto effectCallback_Exit;
146        }
147        // arg1 contains offset of parameter value from start of byte array
148        arg1 = sizeof(effect_param_t) + ((p->psize - 1) / sizeof(int) + 1) * sizeof(int);
149        size = arg1 + p->vsize;
150        array = env->NewByteArray(size);
151        if (array == NULL) {
152            ALOGE("effectCallback: Couldn't allocate byte array for parameter data");
153            goto effectCallback_Exit;
154        }
155        bytes = env->GetByteArrayElements(array, NULL);
156        memcpy(bytes, p, size);
157        env->ReleaseByteArrayElements(array, bytes, 0);
158        obj = array;
159        ALOGV("EVENT_PARAMETER_CHANGED");
160       break;
161    case AudioEffect::EVENT_ERROR:
162        ALOGW("EVENT_ERROR");
163        break;
164    }
165
166    env->CallStaticVoidMethod(
167        callbackInfo->audioEffect_class,
168        fields.midPostNativeEvent,
169        callbackInfo->audioEffect_ref, event, arg1, arg2, obj);
170
171effectCallback_Exit:
172    if (array) {
173        env->DeleteLocalRef(array);
174    }
175
176    if (env->ExceptionCheck()) {
177        env->ExceptionDescribe();
178        env->ExceptionClear();
179    }
180}
181
182// ----------------------------------------------------------------------------
183// This function gets some field IDs, which in turn causes class initialization.
184// It is called from a static block in AudioEffect, which won't run until the
185// first time an instance of this class is used.
186static void
187android_media_AudioEffect_native_init(JNIEnv *env)
188{
189
190    ALOGV("android_media_AudioEffect_native_init");
191
192    fields.clazzEffect = NULL;
193    fields.clazzDesc = NULL;
194
195    // Get the AudioEffect class
196    jclass clazz = env->FindClass(kClassPathName);
197    if (clazz == NULL) {
198        ALOGE("Can't find %s", kClassPathName);
199        return;
200    }
201
202    fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);
203
204    // Get the postEvent method
205    fields.midPostNativeEvent = env->GetStaticMethodID(
206            fields.clazzEffect,
207            "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
208    if (fields.midPostNativeEvent == NULL) {
209        ALOGE("Can't find AudioEffect.%s", "postEventFromNative");
210        return;
211    }
212
213    // Get the variables fields
214    //      nativeTrackInJavaObj
215    fields.fidNativeAudioEffect = env->GetFieldID(
216            fields.clazzEffect,
217            "mNativeAudioEffect", "J");
218    if (fields.fidNativeAudioEffect == NULL) {
219        ALOGE("Can't find AudioEffect.%s", "mNativeAudioEffect");
220        return;
221    }
222    //      fidJniData;
223    fields.fidJniData = env->GetFieldID(
224            fields.clazzEffect,
225            "mJniData", "J");
226    if (fields.fidJniData == NULL) {
227        ALOGE("Can't find AudioEffect.%s", "mJniData");
228        return;
229    }
230
231    clazz = env->FindClass("android/media/audiofx/AudioEffect$Descriptor");
232    if (clazz == NULL) {
233        ALOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class");
234        return;
235    }
236    fields.clazzDesc = (jclass)env->NewGlobalRef(clazz);
237
238    fields.midDescCstor
239            = env->GetMethodID(
240                    fields.clazzDesc,
241                    "<init>",
242                    "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
243    if (fields.midDescCstor == NULL) {
244        ALOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class constructor");
245        return;
246    }
247}
248
249
250static jint
251android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
252        jstring type, jstring uuid, jint priority, jint sessionId, jintArray jId, jobjectArray javadesc)
253{
254    ALOGV("android_media_AudioEffect_native_setup");
255    AudioEffectJniStorage* lpJniStorage = NULL;
256    int lStatus = AUDIOEFFECT_ERROR_NO_MEMORY;
257    AudioEffect* lpAudioEffect = NULL;
258    jint* nId = NULL;
259    const char *typeStr = NULL;
260    const char *uuidStr = NULL;
261    effect_descriptor_t desc;
262    jobject jdesc;
263    char str[EFFECT_STRING_LEN_MAX];
264    jstring jdescType;
265    jstring jdescUuid;
266    jstring jdescConnect;
267    jstring jdescName;
268    jstring jdescImplementor;
269
270    if (type != NULL) {
271        typeStr = env->GetStringUTFChars(type, NULL);
272        if (typeStr == NULL) {  // Out of memory
273            jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
274            goto setup_failure;
275        }
276    }
277
278    if (uuid != NULL) {
279        uuidStr = env->GetStringUTFChars(uuid, NULL);
280        if (uuidStr == NULL) {  // Out of memory
281            jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
282            goto setup_failure;
283        }
284    }
285
286    if (typeStr == NULL && uuidStr == NULL) {
287        lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
288        goto setup_failure;
289    }
290
291    lpJniStorage = new AudioEffectJniStorage();
292    if (lpJniStorage == NULL) {
293        ALOGE("setup: Error creating JNI Storage");
294        goto setup_failure;
295    }
296
297    lpJniStorage->mCallbackData.audioEffect_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
298    // we use a weak reference so the AudioEffect object can be garbage collected.
299    lpJniStorage->mCallbackData.audioEffect_ref = env->NewGlobalRef(weak_this);
300
301    ALOGV("setup: lpJniStorage: %p audioEffect_ref %p audioEffect_class %p, &mCallbackData %p",
302            lpJniStorage,
303            lpJniStorage->mCallbackData.audioEffect_ref,
304            lpJniStorage->mCallbackData.audioEffect_class,
305            &lpJniStorage->mCallbackData);
306
307    if (jId == NULL) {
308        ALOGE("setup: NULL java array for id pointer");
309        lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
310        goto setup_failure;
311    }
312
313    // create the native AudioEffect object
314    lpAudioEffect = new AudioEffect(typeStr,
315                                    uuidStr,
316                                    priority,
317                                    effectCallback,
318                                    &lpJniStorage->mCallbackData,
319                                    sessionId,
320                                    0);
321    if (lpAudioEffect == NULL) {
322        ALOGE("Error creating AudioEffect");
323        goto setup_failure;
324    }
325
326    lStatus = translateError(lpAudioEffect->initCheck());
327    if (lStatus != AUDIOEFFECT_SUCCESS && lStatus != AUDIOEFFECT_ERROR_ALREADY_EXISTS) {
328        ALOGE("AudioEffect initCheck failed %d", lStatus);
329        goto setup_failure;
330    }
331
332    nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
333    if (nId == NULL) {
334        ALOGE("setup: Error retrieving id pointer");
335        lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
336        goto setup_failure;
337    }
338    nId[0] = lpAudioEffect->id();
339    env->ReleasePrimitiveArrayCritical(jId, nId, 0);
340    nId = NULL;
341
342    if (typeStr) {
343        env->ReleaseStringUTFChars(type, typeStr);
344        typeStr = NULL;
345    }
346
347    if (uuidStr) {
348        env->ReleaseStringUTFChars(uuid, uuidStr);
349        uuidStr = NULL;
350    }
351
352    // get the effect descriptor
353    desc = lpAudioEffect->descriptor();
354
355    AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
356    jdescType = env->NewStringUTF(str);
357
358    AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
359    jdescUuid = env->NewStringUTF(str);
360
361    if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
362        jdescConnect = env->NewStringUTF("Auxiliary");
363    } else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC) {
364        jdescConnect = env->NewStringUTF("Pre Processing");
365    } else {
366        jdescConnect = env->NewStringUTF("Insert");
367    }
368
369    jdescName = env->NewStringUTF(desc.name);
370    jdescImplementor = env->NewStringUTF(desc.implementor);
371
372    jdesc = env->NewObject(fields.clazzDesc,
373                           fields.midDescCstor,
374                           jdescType,
375                           jdescUuid,
376                           jdescConnect,
377                           jdescName,
378                           jdescImplementor);
379    env->DeleteLocalRef(jdescType);
380    env->DeleteLocalRef(jdescUuid);
381    env->DeleteLocalRef(jdescConnect);
382    env->DeleteLocalRef(jdescName);
383    env->DeleteLocalRef(jdescImplementor);
384    if (jdesc == NULL) {
385        ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
386        goto setup_failure;
387    }
388
389    env->SetObjectArrayElement(javadesc, 0, jdesc);
390
391    env->SetLongField(thiz, fields.fidNativeAudioEffect, (jlong)lpAudioEffect);
392
393    env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
394
395    return (jint) AUDIOEFFECT_SUCCESS;
396
397    // failures:
398setup_failure:
399
400    if (nId != NULL) {
401        env->ReleasePrimitiveArrayCritical(jId, nId, 0);
402    }
403
404    if (lpAudioEffect) {
405        delete lpAudioEffect;
406    }
407    env->SetLongField(thiz, fields.fidNativeAudioEffect, 0);
408
409    if (lpJniStorage) {
410        delete lpJniStorage;
411    }
412    env->SetLongField(thiz, fields.fidJniData, 0);
413
414    if (uuidStr != NULL) {
415        env->ReleaseStringUTFChars(uuid, uuidStr);
416    }
417
418    if (typeStr != NULL) {
419        env->ReleaseStringUTFChars(type, typeStr);
420    }
421
422    return (jint)lStatus;
423}
424
425
426// ----------------------------------------------------------------------------
427static void android_media_AudioEffect_native_finalize(JNIEnv *env,  jobject thiz) {
428    ALOGV("android_media_AudioEffect_native_finalize jobject: %p\n", thiz);
429
430    // delete the AudioEffect object
431    AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
432        thiz, fields.fidNativeAudioEffect);
433    if (lpAudioEffect) {
434        ALOGV("deleting AudioEffect: %p\n", lpAudioEffect);
435        delete lpAudioEffect;
436    }
437
438    // delete the JNI data
439    AudioEffectJniStorage* lpJniStorage = (AudioEffectJniStorage *)env->GetLongField(
440        thiz, fields.fidJniData);
441    if (lpJniStorage) {
442        ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
443        delete lpJniStorage;
444    }
445}
446
447// ----------------------------------------------------------------------------
448static void android_media_AudioEffect_native_release(JNIEnv *env,  jobject thiz) {
449
450    // do everything a call to finalize would
451    android_media_AudioEffect_native_finalize(env, thiz);
452    // + reset the native resources in the Java object so any attempt to access
453    // them after a call to release fails.
454    env->SetLongField(thiz, fields.fidNativeAudioEffect, 0);
455    env->SetLongField(thiz, fields.fidJniData, 0);
456}
457
458static jint
459android_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
460{
461    // retrieve the AudioEffect object
462    AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
463        thiz, fields.fidNativeAudioEffect);
464
465    if (lpAudioEffect == NULL) {
466        jniThrowException(env, "java/lang/IllegalStateException",
467            "Unable to retrieve AudioEffect pointer for enable()");
468        return AUDIOEFFECT_ERROR_NO_INIT;
469    }
470
471    return (jint) translateError(lpAudioEffect->setEnabled(enabled));
472}
473
474static jboolean
475android_media_AudioEffect_native_getEnabled(JNIEnv *env, jobject thiz)
476{
477    // retrieve the AudioEffect object
478    AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
479        thiz, fields.fidNativeAudioEffect);
480
481    if (lpAudioEffect == NULL) {
482        jniThrowException(env, "java/lang/IllegalStateException",
483            "Unable to retrieve AudioEffect pointer for getEnabled()");
484        return JNI_FALSE;
485    }
486
487    if (lpAudioEffect->getEnabled()) {
488        return JNI_TRUE;
489    } else {
490        return JNI_FALSE;
491    }
492}
493
494
495static jboolean
496android_media_AudioEffect_native_hasControl(JNIEnv *env, jobject thiz)
497{
498    // retrieve the AudioEffect object
499    AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
500        thiz, fields.fidNativeAudioEffect);
501
502    if (lpAudioEffect == NULL) {
503        jniThrowException(env, "java/lang/IllegalStateException",
504            "Unable to retrieve AudioEffect pointer for hasControl()");
505        return JNI_FALSE;
506    }
507
508    if (lpAudioEffect->initCheck() == NO_ERROR) {
509        return JNI_TRUE;
510    } else {
511        return JNI_FALSE;
512    }
513}
514
515static jint android_media_AudioEffect_native_setParameter(JNIEnv *env,
516        jobject thiz, jint psize, jbyteArray pJavaParam, jint vsize,
517        jbyteArray pJavaValue) {
518    // retrieve the AudioEffect object
519    jbyte* lpValue = NULL;
520    jbyte* lpParam = NULL;
521    jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
522    effect_param_t *p;
523    int voffset;
524
525    AudioEffect* lpAudioEffect = (AudioEffect *) env->GetLongField(thiz,
526            fields.fidNativeAudioEffect);
527
528    if (lpAudioEffect == NULL) {
529        jniThrowException(env, "java/lang/IllegalStateException",
530                "Unable to retrieve AudioEffect pointer for setParameter()");
531        return AUDIOEFFECT_ERROR_NO_INIT;
532    }
533
534    if (psize == 0 || vsize == 0 || pJavaParam == NULL || pJavaValue == NULL) {
535        return AUDIOEFFECT_ERROR_BAD_VALUE;
536    }
537
538    // get the pointer for the param from the java array
539    lpParam = (jbyte *) env->GetPrimitiveArrayCritical(pJavaParam, NULL);
540    if (lpParam == NULL) {
541        ALOGE("setParameter: Error retrieving param pointer");
542        goto setParameter_Exit;
543    }
544
545    // get the pointer for the value from the java array
546    lpValue = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValue, NULL);
547    if (lpValue == NULL) {
548        ALOGE("setParameter: Error retrieving value pointer");
549        goto setParameter_Exit;
550    }
551
552    voffset = ((psize - 1) / sizeof(int) + 1) * sizeof(int);
553    p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset + vsize);
554    memcpy(p->data, lpParam, psize);
555    p->psize = psize;
556    memcpy(p->data + voffset, lpValue, vsize);
557    p->vsize = vsize;
558
559    lStatus = lpAudioEffect->setParameter(p);
560    if (lStatus == NO_ERROR) {
561        lStatus = p->status;
562    }
563
564    free(p);
565
566setParameter_Exit:
567
568    if (lpParam != NULL) {
569        env->ReleasePrimitiveArrayCritical(pJavaParam, lpParam, 0);
570    }
571    if (lpValue != NULL) {
572        env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0);
573    }
574    return (jint) translateError(lStatus);
575}
576
577static jint
578android_media_AudioEffect_native_getParameter(JNIEnv *env,
579        jobject thiz, jint psize, jbyteArray pJavaParam,
580        jint vsize, jbyteArray pJavaValue) {
581    // retrieve the AudioEffect object
582    jbyte* lpParam = NULL;
583    jbyte* lpValue = NULL;
584    jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
585    effect_param_t *p;
586    int voffset;
587
588    AudioEffect* lpAudioEffect = (AudioEffect *) env->GetLongField(thiz,
589            fields.fidNativeAudioEffect);
590
591    if (lpAudioEffect == NULL) {
592        jniThrowException(env, "java/lang/IllegalStateException",
593                "Unable to retrieve AudioEffect pointer for getParameter()");
594        return AUDIOEFFECT_ERROR_NO_INIT;
595    }
596
597    if (psize == 0 || vsize == 0 || pJavaParam == NULL || pJavaValue == NULL) {
598        return AUDIOEFFECT_ERROR_BAD_VALUE;
599    }
600
601    // get the pointer for the param from the java array
602    lpParam = (jbyte *) env->GetPrimitiveArrayCritical(pJavaParam, NULL);
603    if (lpParam == NULL) {
604        ALOGE("getParameter: Error retrieving param pointer");
605        goto getParameter_Exit;
606    }
607
608    // get the pointer for the value from the java array
609    lpValue = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValue, NULL);
610    if (lpValue == NULL) {
611        ALOGE("getParameter: Error retrieving value pointer");
612        goto getParameter_Exit;
613    }
614
615    voffset = ((psize - 1) / sizeof(int) + 1) * sizeof(int);
616    p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset + vsize);
617    memcpy(p->data, lpParam, psize);
618    p->psize = psize;
619    p->vsize = vsize;
620
621    lStatus = lpAudioEffect->getParameter(p);
622    if (lStatus == NO_ERROR) {
623        lStatus = p->status;
624        if (lStatus == NO_ERROR) {
625            memcpy(lpValue, p->data + voffset, p->vsize);
626            vsize = p->vsize;
627        }
628    }
629
630    free(p);
631
632getParameter_Exit:
633
634    if (lpParam != NULL) {
635        env->ReleasePrimitiveArrayCritical(pJavaParam, lpParam, 0);
636    }
637    if (lpValue != NULL) {
638        env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0);
639    }
640
641    if (lStatus == NO_ERROR) {
642        return vsize;
643    }
644    return (jint) translateError(lStatus);
645}
646
647static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz,
648        jint cmdCode, jint cmdSize, jbyteArray jCmdData, jint replySize,
649        jbyteArray jReplyData) {
650    jbyte* pCmdData = NULL;
651    jbyte* pReplyData = NULL;
652    jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
653
654    // retrieve the AudioEffect object
655    AudioEffect* lpAudioEffect = (AudioEffect *) env->GetLongField(thiz,
656            fields.fidNativeAudioEffect);
657
658    if (lpAudioEffect == NULL) {
659        jniThrowException(env, "java/lang/IllegalStateException",
660                "Unable to retrieve AudioEffect pointer for setParameter()");
661        return AUDIOEFFECT_ERROR_NO_INIT;
662    }
663
664    if ((cmdSize != 0 && jCmdData == NULL) || (replySize != 0 && jReplyData == NULL)) {
665        return AUDIOEFFECT_ERROR_BAD_VALUE;
666    }
667
668    // get the pointer for the command from the java array
669    if (cmdSize != 0) {
670        pCmdData = (jbyte *) env->GetPrimitiveArrayCritical(jCmdData, NULL);
671        if (pCmdData == NULL) {
672            ALOGE("setParameter: Error retrieving command pointer");
673            goto command_Exit;
674        }
675    }
676
677    // get the pointer for the reply from the java array
678    if (replySize != 0 && jReplyData != NULL) {
679        pReplyData = (jbyte *) env->GetPrimitiveArrayCritical(jReplyData, NULL);
680        if (pReplyData == NULL) {
681            ALOGE("setParameter: Error retrieving reply pointer");
682            goto command_Exit;
683        }
684    }
685
686    lStatus = translateError(lpAudioEffect->command((uint32_t)cmdCode,
687                                                    (uint32_t)cmdSize,
688                                                    pCmdData,
689                                                    (uint32_t *)&replySize,
690                                                    pReplyData));
691
692command_Exit:
693
694    if (pCmdData != NULL) {
695        env->ReleasePrimitiveArrayCritical(jCmdData, pCmdData, 0);
696    }
697    if (pReplyData != NULL) {
698        env->ReleasePrimitiveArrayCritical(jReplyData, pReplyData, 0);
699    }
700
701    if (lStatus == NO_ERROR) {
702        return replySize;
703    }
704    return lStatus;
705}
706
707static jobjectArray
708android_media_AudioEffect_native_queryEffects(JNIEnv *env, jclass clazz __unused)
709{
710    effect_descriptor_t desc;
711    char str[EFFECT_STRING_LEN_MAX];
712    uint32_t totalEffectsCount = 0;
713    uint32_t returnedEffectsCount = 0;
714    uint32_t i = 0;
715    jstring jdescType;
716    jstring jdescUuid;
717    jstring jdescConnect;
718    jstring jdescName;
719    jstring jdescImplementor;
720    jobject jdesc;
721    jobjectArray ret;
722
723    if (AudioEffect::queryNumberEffects(&totalEffectsCount) != NO_ERROR) {
724        return NULL;
725    }
726
727    jobjectArray temp = env->NewObjectArray(totalEffectsCount, fields.clazzDesc, NULL);
728    if (temp == NULL) {
729        return temp;
730    }
731
732    ALOGV("queryEffects() totalEffectsCount: %d", totalEffectsCount);
733
734    for (i = 0; i < totalEffectsCount; i++) {
735        if (AudioEffect::queryEffect(i, &desc) != NO_ERROR) {
736            goto queryEffects_failure;
737        }
738
739        if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
740            jdescConnect = env->NewStringUTF("Auxiliary");
741        } else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT) {
742            jdescConnect = env->NewStringUTF("Insert");
743        } else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC) {
744            jdescConnect = env->NewStringUTF("Pre Processing");
745        } else {
746            continue;
747        }
748
749        AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
750        jdescType = env->NewStringUTF(str);
751
752        AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
753        jdescUuid = env->NewStringUTF(str);
754
755        jdescName = env->NewStringUTF(desc.name);
756        jdescImplementor = env->NewStringUTF(desc.implementor);
757
758        jdesc = env->NewObject(fields.clazzDesc,
759                               fields.midDescCstor,
760                               jdescType,
761                               jdescUuid,
762                               jdescConnect,
763                               jdescName,
764                               jdescImplementor);
765        env->DeleteLocalRef(jdescType);
766        env->DeleteLocalRef(jdescUuid);
767        env->DeleteLocalRef(jdescConnect);
768        env->DeleteLocalRef(jdescName);
769        env->DeleteLocalRef(jdescImplementor);
770        if (jdesc == NULL) {
771            ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
772            goto queryEffects_failure;
773        }
774
775        env->SetObjectArrayElement(temp, returnedEffectsCount++, jdesc);
776   }
777
778    if (returnedEffectsCount == 0) {
779        goto queryEffects_failure;
780    }
781    ret = env->NewObjectArray(returnedEffectsCount, fields.clazzDesc, NULL);
782    if (ret == NULL) {
783        goto queryEffects_failure;
784    }
785    for (i = 0; i < returnedEffectsCount; i++) {
786        env->SetObjectArrayElement(ret, i, env->GetObjectArrayElement(temp, i));
787    }
788    env->DeleteLocalRef(temp);
789    return ret;
790
791queryEffects_failure:
792
793    if (temp != NULL) {
794        env->DeleteLocalRef(temp);
795    }
796    return NULL;
797
798}
799
800
801
802static jobjectArray
803android_media_AudioEffect_native_queryPreProcessings(JNIEnv *env, jclass clazz __unused,
804                                                     jint audioSession)
805{
806    // kDefaultNumEffects is a "reasonable" value ensuring that only one query will be enough on
807    // most devices to get all active audio pre processing on a given session.
808    static const uint32_t kDefaultNumEffects = 5;
809
810    effect_descriptor_t *descriptors = new effect_descriptor_t[kDefaultNumEffects];
811    uint32_t numEffects = kDefaultNumEffects;
812
813    status_t status = AudioEffect::queryDefaultPreProcessing(audioSession,
814                                           descriptors,
815                                           &numEffects);
816    if ((status != NO_ERROR && status != NO_MEMORY) ||
817            numEffects == 0) {
818        delete[] descriptors;
819        return NULL;
820    }
821    if (status == NO_MEMORY) {
822        delete [] descriptors;
823        descriptors = new effect_descriptor_t[numEffects];
824        status = AudioEffect::queryDefaultPreProcessing(audioSession,
825                                               descriptors,
826                                               &numEffects);
827    }
828    if (status != NO_ERROR || numEffects == 0) {
829        delete[] descriptors;
830        return NULL;
831    }
832    ALOGV("queryDefaultPreProcessing() got %d effects", numEffects);
833
834    jobjectArray ret = env->NewObjectArray(numEffects, fields.clazzDesc, NULL);
835    if (ret == NULL) {
836        delete[] descriptors;
837        return ret;
838    }
839
840    char str[EFFECT_STRING_LEN_MAX];
841    jstring jdescType;
842    jstring jdescUuid;
843    jstring jdescConnect;
844    jstring jdescName;
845    jstring jdescImplementor;
846    jobject jdesc;
847
848    for (uint32_t i = 0; i < numEffects; i++) {
849
850        AudioEffect::guidToString(&descriptors[i].type, str, EFFECT_STRING_LEN_MAX);
851        jdescType = env->NewStringUTF(str);
852        AudioEffect::guidToString(&descriptors[i].uuid, str, EFFECT_STRING_LEN_MAX);
853        jdescUuid = env->NewStringUTF(str);
854        jdescConnect = env->NewStringUTF("Pre Processing");
855        jdescName = env->NewStringUTF(descriptors[i].name);
856        jdescImplementor = env->NewStringUTF(descriptors[i].implementor);
857
858        jdesc = env->NewObject(fields.clazzDesc,
859                               fields.midDescCstor,
860                               jdescType,
861                               jdescUuid,
862                               jdescConnect,
863                               jdescName,
864                               jdescImplementor);
865        env->DeleteLocalRef(jdescType);
866        env->DeleteLocalRef(jdescUuid);
867        env->DeleteLocalRef(jdescConnect);
868        env->DeleteLocalRef(jdescName);
869        env->DeleteLocalRef(jdescImplementor);
870        if (jdesc == NULL) {
871            ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
872            env->DeleteLocalRef(ret);
873            return NULL;;
874        }
875
876        env->SetObjectArrayElement(ret, i, jdesc);
877   }
878
879   return ret;
880}
881
882// ----------------------------------------------------------------------------
883
884// Dalvik VM type signatures
885static JNINativeMethod gMethods[] = {
886    {"native_init",          "()V",      (void *)android_media_AudioEffect_native_init},
887    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;II[I[Ljava/lang/Object;)I",
888                                         (void *)android_media_AudioEffect_native_setup},
889    {"native_finalize",      "()V",      (void *)android_media_AudioEffect_native_finalize},
890    {"native_release",       "()V",      (void *)android_media_AudioEffect_native_release},
891    {"native_setEnabled",    "(Z)I",      (void *)android_media_AudioEffect_native_setEnabled},
892    {"native_getEnabled",    "()Z",      (void *)android_media_AudioEffect_native_getEnabled},
893    {"native_hasControl",    "()Z",      (void *)android_media_AudioEffect_native_hasControl},
894    {"native_setParameter",  "(I[BI[B)I",  (void *)android_media_AudioEffect_native_setParameter},
895    {"native_getParameter",  "(I[BI[B)I",  (void *)android_media_AudioEffect_native_getParameter},
896    {"native_command",       "(II[BI[B)I", (void *)android_media_AudioEffect_native_command},
897    {"native_query_effects", "()[Ljava/lang/Object;", (void *)android_media_AudioEffect_native_queryEffects},
898    {"native_query_pre_processing", "(I)[Ljava/lang/Object;",
899            (void *)android_media_AudioEffect_native_queryPreProcessings},
900};
901
902
903// ----------------------------------------------------------------------------
904
905extern int register_android_media_visualizer(JNIEnv *env);
906
907int register_android_media_AudioEffect(JNIEnv *env)
908{
909    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
910}
911
912jint JNI_OnLoad(JavaVM* vm, void* reserved __unused)
913{
914
915    JNIEnv* env = NULL;
916    jint result = -1;
917
918    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
919        ALOGE("ERROR: GetEnv failed\n");
920        goto bail;
921    }
922    assert(env != NULL);
923
924    if (register_android_media_AudioEffect(env) < 0) {
925        ALOGE("ERROR: AudioEffect native registration failed\n");
926        goto bail;
927    }
928
929    if (register_android_media_visualizer(env) < 0) {
930        ALOGE("ERROR: Visualizer native registration failed\n");
931        goto bail;
932    }
933
934    /* success -- return valid version number */
935    result = JNI_VERSION_1_4;
936
937bail:
938    return result;
939}
940
941