/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioEffects-JNI"
#include <utils/Log.h>
#include <nativehelper/jni.h>
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include "media/AudioEffect.h"
#include <ScopedUtfChars.h>
using namespace android;
#define AUDIOEFFECT_SUCCESS 0
#define AUDIOEFFECT_ERROR -1
#define AUDIOEFFECT_ERROR_ALREADY_EXISTS -2
#define AUDIOEFFECT_ERROR_NO_INIT -3
#define AUDIOEFFECT_ERROR_BAD_VALUE -4
#define AUDIOEFFECT_ERROR_INVALID_OPERATION -5
#define AUDIOEFFECT_ERROR_NO_MEMORY -6
#define AUDIOEFFECT_ERROR_DEAD_OBJECT -7
// ----------------------------------------------------------------------------
static const char* const kClassPathName = "android/media/audiofx/AudioEffect";
struct fields_t {
// these fields provide access from C++ to the...
jclass clazzEffect; // AudioEffect class
jmethodID midPostNativeEvent; // event post callback method
jfieldID fidNativeAudioEffect; // stores in Java the native AudioEffect object
jfieldID fidJniData; // stores in Java additional resources used by the native AudioEffect
jclass clazzDesc; // AudioEffect.Descriptor class
jmethodID midDescCstor; // AudioEffect.Descriptor class constructor
};
static fields_t fields;
struct effect_callback_cookie {
jclass audioEffect_class; // AudioEffect class
jobject audioEffect_ref; // AudioEffect object instance
};
// ----------------------------------------------------------------------------
class AudioEffectJniStorage {
public:
effect_callback_cookie mCallbackData;
AudioEffectJniStorage() {
}
~AudioEffectJniStorage() {
}
};
static jint translateError(int code) {
switch(code) {
case NO_ERROR:
return AUDIOEFFECT_SUCCESS;
case ALREADY_EXISTS:
return AUDIOEFFECT_ERROR_ALREADY_EXISTS;
case NO_INIT:
return AUDIOEFFECT_ERROR_NO_INIT;
case BAD_VALUE:
return AUDIOEFFECT_ERROR_BAD_VALUE;
case INVALID_OPERATION:
return AUDIOEFFECT_ERROR_INVALID_OPERATION;
case NO_MEMORY:
return AUDIOEFFECT_ERROR_NO_MEMORY;
case DEAD_OBJECT:
return AUDIOEFFECT_ERROR_DEAD_OBJECT;
default:
return AUDIOEFFECT_ERROR;
}
}
static Mutex sLock;
// ----------------------------------------------------------------------------
static void effectCallback(int event, void* user, void *info) {
effect_param_t *p;
int arg1 = 0;
int arg2 = 0;
jobject obj = NULL;
jbyteArray array = NULL;
jbyte *bytes;
bool param;
size_t size;
effect_callback_cookie *callbackInfo = (effect_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
ALOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p",
callbackInfo,
callbackInfo->audioEffect_ref,
callbackInfo->audioEffect_class);
if (!user || !env) {
ALOGW("effectCallback error user %p, env %p", user, env);
return;
}
switch (event) {
case AudioEffect::EVENT_CONTROL_STATUS_CHANGED:
if (info == 0) {
ALOGW("EVENT_CONTROL_STATUS_CHANGED info == NULL");
goto effectCallback_Exit;
}
param = *(bool *)info;
arg1 = (int)param;
ALOGV("EVENT_CONTROL_STATUS_CHANGED");
break;
case AudioEffect::EVENT_ENABLE_STATUS_CHANGED:
if (info == 0) {
ALOGW("EVENT_ENABLE_STATUS_CHANGED info == NULL");
goto effectCallback_Exit;
}
param = *(bool *)info;
arg1 = (int)param;
ALOGV("EVENT_ENABLE_STATUS_CHANGED");
break;
case AudioEffect::EVENT_PARAMETER_CHANGED:
if (info == 0) {
ALOGW("EVENT_PARAMETER_CHANGED info == NULL");
goto effectCallback_Exit;
}
p = (effect_param_t *)info;
if (p->psize == 0 || p->vsize == 0) {
goto effectCallback_Exit;
}
// arg1 contains offset of parameter value from start of byte array
arg1 = sizeof(effect_param_t) + ((p->psize - 1) / sizeof(int) + 1) * sizeof(int);
size = arg1 + p->vsize;
array = env->NewByteArray(size);
if (array == NULL) {
ALOGE("effectCallback: Couldn't allocate byte array for parameter data");
goto effectCallback_Exit;
}
bytes = env->GetByteArrayElements(array, NULL);
memcpy(bytes, p, size);
env->ReleaseByteArrayElements(array, bytes, 0);
obj = array;
ALOGV("EVENT_PARAMETER_CHANGED");
break;
case AudioEffect::EVENT_ERROR:
ALOGW("EVENT_ERROR");
break;
}
env->CallStaticVoidMethod(
callbackInfo->audioEffect_class,
fields.midPostNativeEvent,
callbackInfo->audioEffect_ref, event, arg1, arg2, obj);
effectCallback_Exit:
if (array) {
env->DeleteLocalRef(array);
}
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
}
// ----------------------------------------------------------------------------
static sp<AudioEffect> getAudioEffect(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(sLock);
AudioEffect* const ae =
(AudioEffect*)env->GetLongField(thiz, fields.fidNativeAudioEffect);
return sp<AudioEffect>(ae);
}
static sp<AudioEffect> setAudioEffect(JNIEnv* env, jobject thiz,
const sp<AudioEffect>& ae)
{
Mutex::Autolock l(sLock);
sp<AudioEffect> old =
(AudioEffect*)env->GetLongField(thiz, fields.fidNativeAudioEffect);
if (ae.get()) {
ae->incStrong((void*)setAudioEffect);
}
if (old != 0) {
old->decStrong((void*)setAudioEffect);
}
env->SetLongField(thiz, fields.fidNativeAudioEffect, (jlong)ae.get());
return old;
}
// ----------------------------------------------------------------------------
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in AudioEffect, which won't run until the
// first time an instance of this class is used.
static void
android_media_AudioEffect_native_init(JNIEnv *env)
{
ALOGV("android_media_AudioEffect_native_init");
fields.clazzEffect = NULL;
fields.clazzDesc = NULL;
// Get the AudioEffect class
jclass clazz = env->FindClass(kClassPathName);
if (clazz == NULL) {
ALOGE("Can't find %s", kClassPathName);
return;
}
fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);
// Get the postEvent method
fields.midPostNativeEvent = env->GetStaticMethodID(
fields.clazzEffect,
"postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.midPostNativeEvent == NULL) {
ALOGE("Can't find AudioEffect.%s", "postEventFromNative");
return;
}
// Get the variables fields
// nativeTrackInJavaObj
fields.fidNativeAudioEffect = env->GetFieldID(
fields.clazzEffect,
"mNativeAudioEffect", "J");
if (fields.fidNativeAudioEffect == NULL) {
ALOGE("Can't find AudioEffect.%s", "mNativeAudioEffect");
return;
}
// fidJniData;
fields.fidJniData = env->GetFieldID(
fields.clazzEffect,
"mJniData", "J");
if (fields.fidJniData == NULL) {
ALOGE("Can't find AudioEffect.%s", "mJniData");
return;
}
clazz = env->FindClass("android/media/audiofx/AudioEffect$Descriptor");
if (clazz == NULL) {
ALOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class");
return;
}
fields.clazzDesc = (jclass)env->NewGlobalRef(clazz);
fields.midDescCstor
= env->GetMethodID(
fields.clazzDesc,
"<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
if (fields.midDescCstor == NULL) {
ALOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class constructor");
return;
}
}
static jint
android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jstring type, jstring uuid, jint priority, jint sessionId, jintArray jId,
jobjectArray javadesc, jstring opPackageName)
{
ALOGV("android_media_AudioEffect_native_setup");
AudioEffectJniStorage* lpJniStorage = NULL;
int lStatus = AUDIOEFFECT_ERROR_NO_MEMORY;
sp<AudioEffect> lpAudioEffect;
jint* nId = NULL;
const char *typeStr = NULL;
const char *uuidStr = NULL;
effect_descriptor_t desc;
jobject jdesc;
char str[EFFECT_STRING_LEN_MAX];
jstring jdescType;
jstring jdescUuid;
jstring jdescConnect;
jstring jdescName;
jstring jdescImplementor;
ScopedUtfChars opPackageNameStr(env, opPackageName);
setAudioEffect(env, thiz, 0);
if (type != NULL) {
typeStr = env->GetStringUTFChars(type, NULL);
if (typeStr == NULL) { // Out of memory
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
goto setup_failure;
}
}
if (uuid != NULL) {
uuidStr = env->GetStringUTFChars(uuid, NULL);
if (uuidStr == NULL) { // Out of memory
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
goto setup_failure;
}
}
if (typeStr == NULL && uuidStr == NULL) {
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
goto setup_failure;
}
lpJniStorage = new AudioEffectJniStorage();
if (lpJniStorage == NULL) {
ALOGE("setup: Error creating JNI Storage");
goto setup_failure;
}
lpJniStorage->mCallbackData.audioEffect_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
// we use a weak reference so the AudioEffect object can be garbage collected.
lpJniStorage->mCallbackData.audioEffect_ref = env->NewGlobalRef(weak_this);
ALOGV("setup: lpJniStorage: %p audioEffect_ref %p audioEffect_class %p, &mCallbackData %p",
lpJniStorage,
lpJniStorage->mCallbackData.audioEffect_ref,
lpJniStorage->mCallbackData.audioEffect_class,
&lpJniStorage->mCallbackData);
if (jId == NULL) {
ALOGE("setup: NULL java array for id pointer");
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
goto setup_failure;
}
// create the native AudioEffect object
lpAudioEffect = new AudioEffect(typeStr,
String16(opPackageNameStr.c_str()),
uuidStr,
priority,
effectCallback,
&lpJniStorage->mCallbackData,
(audio_session_t) sessionId,
0);
if (lpAudioEffect == 0) {
ALOGE("Error creating AudioEffect");
goto setup_failure;
}
lStatus = translateError(lpAudioEffect->initCheck());
if (lStatus != AUDIOEFFECT_SUCCESS && lStatus != AUDIOEFFECT_ERROR_ALREADY_EXISTS) {
ALOGE("AudioEffect initCheck failed %d", lStatus);
goto setup_failure;
}
nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
if (nId == NULL) {
ALOGE("setup: Error retrieving id pointer");
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
goto setup_failure;
}
nId[0] = lpAudioEffect->id();
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
nId = NULL;
if (typeStr) {
env->ReleaseStringUTFChars(type, typeStr);
typeStr = NULL;
}
if (uuidStr) {
env->ReleaseStringUTFChars(uuid, uuidStr);
uuidStr = NULL;
}
// get the effect descriptor
desc = lpAudioEffect->descriptor();
AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
jdescType = env->NewStringUTF(str);
AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
jdescUuid = env->NewStringUTF(str);
if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
jdescConnect = env->NewStringUTF("Auxiliary");
} else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC) {
jdescConnect = env->NewStringUTF("Pre Processing");
} else {
jdescConnect = env->NewStringUTF("Insert");
}
jdescName = env->NewStringUTF(desc.name);
jdescImplementor = env->NewStringUTF(desc.implementor);
jdesc = env->NewObject(fields.clazzDesc,
fields.midDescCstor,
jdescType,
jdescUuid,
jdescConnect,
jdescName,
jdescImplementor);
env->DeleteLocalRef(jdescType);
env->DeleteLocalRef(jdescUuid);
env->DeleteLocalRef(jdescConnect);
env->DeleteLocalRef(jdescName);
env->DeleteLocalRef(jdescImplementor);
if (jdesc == NULL) {
ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
goto setup_failure;
}
env->SetObjectArrayElement(javadesc, 0, jdesc);
setAudioEffect(env, thiz, lpAudioEffect);
env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
return (jint) AUDIOEFFECT_SUCCESS;
// failures:
setup_failure:
if (nId != NULL) {
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
}
if (lpJniStorage) {
env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_class);
env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_ref);
delete lpJniStorage;
}
env->SetLongField(thiz, fields.fidJniData, 0);
if (uuidStr != NULL) {
env->ReleaseStringUTFChars(uuid, uuidStr);
}
if (typeStr != NULL) {
env->ReleaseStringUTFChars(type, typeStr);
}
return (jint)lStatus;
}
// ----------------------------------------------------------------------------
static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz) {
sp<AudioEffect> lpAudioEffect = setAudioEffect(env, thiz, 0);
if (lpAudioEffect == 0) {
return;
}
// delete the JNI data
AudioEffectJniStorage* lpJniStorage =
(AudioEffectJniStorage *)env->GetLongField(thiz, fields.fidJniData);
// reset the native resources in the Java object so any attempt to access
// them after a call to release fails.
env->SetLongField(thiz, fields.fidJniData, 0);
if (lpJniStorage) {
ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_class);
env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_ref);
delete lpJniStorage;
}
}
// ----------------------------------------------------------------------------
static void android_media_AudioEffect_native_finalize(JNIEnv *env, jobject thiz) {
ALOGV("android_media_AudioEffect_native_finalize jobject: %p\n", thiz);
android_media_AudioEffect_native_release(env, thiz);
}
static jint
android_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
{
sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for enable()");
return AUDIOEFFECT_ERROR_NO_INIT;
}
return (jint) translateError(lpAudioEffect->setEnabled(enabled));
}
static jboolean
android_media_AudioEffect_native_getEnabled(JNIEnv *env, jobject thiz)
{
sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for getEnabled()");
return JNI_FALSE;
}
if (lpAudioEffect->getEnabled()) {
return JNI_TRUE;
} else {
return JNI_FALSE;
}
}
static jboolean
android_media_AudioEffect_native_hasControl(JNIEnv *env, jobject thiz)
{
sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for hasControl()");
return JNI_FALSE;
}
if (lpAudioEffect->initCheck() == NO_ERROR) {
return JNI_TRUE;
} else {
return JNI_FALSE;
}
}
static jint android_media_AudioEffect_native_setParameter(JNIEnv *env,
jobject thiz, jint psize, jbyteArray pJavaParam, jint vsize,
jbyteArray pJavaValue) {
// retrieve the AudioEffect object
jbyte* lpValue = NULL;
jbyte* lpParam = NULL;
jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
effect_param_t *p;
int voffset;
sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for setParameter()");
return AUDIOEFFECT_ERROR_NO_INIT;
}
if (psize == 0 || vsize == 0 || pJavaParam == NULL || pJavaValue == NULL) {
return AUDIOEFFECT_ERROR_BAD_VALUE;
}
// get the pointer for the param from the java array
lpParam = (jbyte *) env->GetPrimitiveArrayCritical(pJavaParam, NULL);
if (lpParam == NULL) {
ALOGE("setParameter: Error retrieving param pointer");
goto setParameter_Exit;
}
// get the pointer for the value from the java array
lpValue = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValue, NULL);
if (lpValue == NULL) {
ALOGE("setParameter: Error retrieving value pointer");
goto setParameter_Exit;
}
voffset = ((psize - 1) / sizeof(int) + 1) * sizeof(int);
p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset + vsize);
memcpy(p->data, lpParam, psize);
p->psize = psize;
memcpy(p->data + voffset, lpValue, vsize);
p->vsize = vsize;
lStatus = lpAudioEffect->setParameter(p);
if (lStatus == NO_ERROR) {
lStatus = p->status;
}
free(p);
setParameter_Exit:
if (lpParam != NULL) {
env->ReleasePrimitiveArrayCritical(pJavaParam, lpParam, 0);
}
if (lpValue != NULL) {
env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0);
}
return (jint) translateError(lStatus);
}
static jint
android_media_AudioEffect_native_getParameter(JNIEnv *env,
jobject thiz, jint psize, jbyteArray pJavaParam,
jint vsize, jbyteArray pJavaValue) {
// retrieve the AudioEffect object
jbyte* lpParam = NULL;
jbyte* lpValue = NULL;
jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
effect_param_t *p;
int voffset;
sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for getParameter()");
return AUDIOEFFECT_ERROR_NO_INIT;
}
if (psize == 0 || vsize == 0 || pJavaParam == NULL || pJavaValue == NULL) {
return AUDIOEFFECT_ERROR_BAD_VALUE;
}
// get the pointer for the param from the java array
lpParam = (jbyte *) env->GetPrimitiveArrayCritical(pJavaParam, NULL);
if (lpParam == NULL) {
ALOGE("getParameter: Error retrieving param pointer");
goto getParameter_Exit;
}
// get the pointer for the value from the java array
lpValue = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValue, NULL);
if (lpValue == NULL) {
ALOGE("getParameter: Error retrieving value pointer");
goto getParameter_Exit;
}
voffset = ((psize - 1) / sizeof(int) + 1) * sizeof(int);
p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset + vsize);
memcpy(p->data, lpParam, psize);
p->psize = psize;
p->vsize = vsize;
lStatus = lpAudioEffect->getParameter(p);
if (lStatus == NO_ERROR) {
lStatus = p->status;
if (lStatus == NO_ERROR) {
memcpy(lpValue, p->data + voffset, p->vsize);
vsize = p->vsize;
}
}
free(p);
getParameter_Exit:
if (lpParam != NULL) {
env->ReleasePrimitiveArrayCritical(pJavaParam, lpParam, 0);
}
if (lpValue != NULL) {
env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0);
}
if (lStatus == NO_ERROR) {
return vsize;
}
return (jint) translateError(lStatus);
}
static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz,
jint cmdCode, jint cmdSize, jbyteArray jCmdData, jint replySize,
jbyteArray jReplyData) {
jbyte* pCmdData = NULL;
jbyte* pReplyData = NULL;
jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for setParameter()");
return AUDIOEFFECT_ERROR_NO_INIT;
}
if ((cmdSize != 0 && jCmdData == NULL) || (replySize != 0 && jReplyData == NULL)) {
return AUDIOEFFECT_ERROR_BAD_VALUE;
}
// get the pointer for the command from the java array
if (cmdSize != 0) {
pCmdData = (jbyte *) env->GetPrimitiveArrayCritical(jCmdData, NULL);
if (pCmdData == NULL) {
ALOGE("setParameter: Error retrieving command pointer");
goto command_Exit;
}
}
// get the pointer for the reply from the java array
if (replySize != 0 && jReplyData != NULL) {
pReplyData = (jbyte *) env->GetPrimitiveArrayCritical(jReplyData, NULL);
if (pReplyData == NULL) {
ALOGE("setParameter: Error retrieving reply pointer");
goto command_Exit;
}
}
lStatus = translateError(lpAudioEffect->command((uint32_t)cmdCode,
(uint32_t)cmdSize,
pCmdData,
(uint32_t *)&replySize,
pReplyData));
command_Exit:
if (pCmdData != NULL) {
env->ReleasePrimitiveArrayCritical(jCmdData, pCmdData, 0);
}
if (pReplyData != NULL) {
env->ReleasePrimitiveArrayCritical(jReplyData, pReplyData, 0);
}
if (lStatus == NO_ERROR) {
return replySize;
}
return lStatus;
}
static jobjectArray
android_media_AudioEffect_native_queryEffects(JNIEnv *env, jclass clazz __unused)
{
effect_descriptor_t desc;
char str[EFFECT_STRING_LEN_MAX];
uint32_t totalEffectsCount = 0;
uint32_t returnedEffectsCount = 0;
uint32_t i = 0;
jstring jdescType;
jstring jdescUuid;
jstring jdescConnect;
jstring jdescName;
jstring jdescImplementor;
jobject jdesc;
jobjectArray ret;
if (AudioEffect::queryNumberEffects(&totalEffectsCount) != NO_ERROR) {
return NULL;
}
jobjectArray temp = env->NewObjectArray(totalEffectsCount, fields.clazzDesc, NULL);
if (temp == NULL) {
return temp;
}
ALOGV("queryEffects() totalEffectsCount: %d", totalEffectsCount);
for (i = 0; i < totalEffectsCount; i++) {
if (AudioEffect::queryEffect(i, &desc) != NO_ERROR) {
goto queryEffects_failure;
}
if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
jdescConnect = env->NewStringUTF("Auxiliary");
} else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT) {
jdescConnect = env->NewStringUTF("Insert");
} else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC) {
jdescConnect = env->NewStringUTF("Pre Processing");
} else {
continue;
}
AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
jdescType = env->NewStringUTF(str);
AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
jdescUuid = env->NewStringUTF(str);
jdescName = env->NewStringUTF(desc.name);
jdescImplementor = env->NewStringUTF(desc.implementor);
jdesc = env->NewObject(fields.clazzDesc,
fields.midDescCstor,
jdescType,
jdescUuid,
jdescConnect,
jdescName,
jdescImplementor);
env->DeleteLocalRef(jdescType);
env->DeleteLocalRef(jdescUuid);
env->DeleteLocalRef(jdescConnect);
env->DeleteLocalRef(jdescName);
env->DeleteLocalRef(jdescImplementor);
if (jdesc == NULL) {
ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
goto queryEffects_failure;
}
env->SetObjectArrayElement(temp, returnedEffectsCount++, jdesc);
}
if (returnedEffectsCount == 0) {
goto queryEffects_failure;
}
ret = env->NewObjectArray(returnedEffectsCount, fields.clazzDesc, NULL);
if (ret == NULL) {
goto queryEffects_failure;
}
for (i = 0; i < returnedEffectsCount; i++) {
env->SetObjectArrayElement(ret, i, env->GetObjectArrayElement(temp, i));
}
env->DeleteLocalRef(temp);
return ret;
queryEffects_failure:
if (temp != NULL) {
env->DeleteLocalRef(temp);
}
return NULL;
}
static jobjectArray
android_media_AudioEffect_native_queryPreProcessings(JNIEnv *env, jclass clazz __unused,
jint audioSession)
{
effect_descriptor_t *descriptors = new effect_descriptor_t[AudioEffect::kMaxPreProcessing];
uint32_t numEffects = AudioEffect::kMaxPreProcessing;
status_t status = AudioEffect::queryDefaultPreProcessing((audio_session_t) audioSession,
descriptors,
&numEffects);
if (status != NO_ERROR || numEffects == 0) {
delete[] descriptors;
return NULL;
}
ALOGV("queryDefaultPreProcessing() got %d effects", numEffects);
jobjectArray ret = env->NewObjectArray(numEffects, fields.clazzDesc, NULL);
if (ret == NULL) {
delete[] descriptors;
return ret;
}
char str[EFFECT_STRING_LEN_MAX];
jstring jdescType;
jstring jdescUuid;
jstring jdescConnect;
jstring jdescName;
jstring jdescImplementor;
jobject jdesc;
for (uint32_t i = 0; i < numEffects; i++) {
AudioEffect::guidToString(&descriptors[i].type, str, EFFECT_STRING_LEN_MAX);
jdescType = env->NewStringUTF(str);
AudioEffect::guidToString(&descriptors[i].uuid, str, EFFECT_STRING_LEN_MAX);
jdescUuid = env->NewStringUTF(str);
jdescConnect = env->NewStringUTF("Pre Processing");
jdescName = env->NewStringUTF(descriptors[i].name);
jdescImplementor = env->NewStringUTF(descriptors[i].implementor);
jdesc = env->NewObject(fields.clazzDesc,
fields.midDescCstor,
jdescType,
jdescUuid,
jdescConnect,
jdescName,
jdescImplementor);
env->DeleteLocalRef(jdescType);
env->DeleteLocalRef(jdescUuid);
env->DeleteLocalRef(jdescConnect);
env->DeleteLocalRef(jdescName);
env->DeleteLocalRef(jdescImplementor);
if (jdesc == NULL) {
ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
env->DeleteLocalRef(ret);
return NULL;;
}
env->SetObjectArrayElement(ret, i, jdesc);
}
return ret;
}
// ----------------------------------------------------------------------------
// Dalvik VM type signatures
static const JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_AudioEffect_native_init},
{"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;II[I[Ljava/lang/Object;Ljava/lang/String;)I",
(void *)android_media_AudioEffect_native_setup},
{"native_finalize", "()V", (void *)android_media_AudioEffect_native_finalize},
{"native_release", "()V", (void *)android_media_AudioEffect_native_release},
{"native_setEnabled", "(Z)I", (void *)android_media_AudioEffect_native_setEnabled},
{"native_getEnabled", "()Z", (void *)android_media_AudioEffect_native_getEnabled},
{"native_hasControl", "()Z", (void *)android_media_AudioEffect_native_hasControl},
{"native_setParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_setParameter},
{"native_getParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_getParameter},
{"native_command", "(II[BI[B)I", (void *)android_media_AudioEffect_native_command},
{"native_query_effects", "()[Ljava/lang/Object;", (void *)android_media_AudioEffect_native_queryEffects},
{"native_query_pre_processing", "(I)[Ljava/lang/Object;",
(void *)android_media_AudioEffect_native_queryPreProcessings},
};
// ----------------------------------------------------------------------------
extern int register_android_media_visualizer(JNIEnv *env);
int register_android_media_AudioEffect(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
}
jint JNI_OnLoad(JavaVM* vm, void* reserved __unused)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed\n");
goto bail;
}
assert(env != NULL);
if (register_android_media_AudioEffect(env) < 0) {
ALOGE("ERROR: AudioEffect native registration failed\n");
goto bail;
}
if (register_android_media_visualizer(env) < 0) {
ALOGE("ERROR: Visualizer native registration failed\n");
goto bail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
bail:
return result;
}