android_frameworks_base/media/jni/audioeffect/android_media_AudioEffect.cpp
Eric Laurent 0f7f4ece1b Added APIs for audio preprocessing
Added APIs to control pre processes applied on captured audio.
Those APIs are still hidden until reviewed by API council.

Three types of standard pre processes are supported:
- Automatic Gain Control (AGC) by AutomaticGainControl class
- Acoustic Echo Cancellation (AEC) by AcousticEchoCanceler class
- Noise Suppression (NS) by NoiseSuppressor class

A method is added to AudioEffect class to query audio pre processings
applied by default by the platform on a given AudioRecord session ID.

Change-Id: I0b9fceeb8c704dd06319c3b52b85c96fe871d51d
2011-07-25 14:39:00 -07:00

918 lines
30 KiB
C++

/*
* 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"
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 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();
LOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p",
callbackInfo,
callbackInfo->audioEffect_ref,
callbackInfo->audioEffect_class);
if (!user || !env) {
LOGW("effectCallback error user %p, env %p", user, env);
return;
}
switch (event) {
case AudioEffect::EVENT_CONTROL_STATUS_CHANGED:
if (info == 0) {
LOGW("EVENT_CONTROL_STATUS_CHANGED info == NULL");
goto effectCallback_Exit;
}
param = *(bool *)info;
arg1 = (int)param;
LOGV("EVENT_CONTROL_STATUS_CHANGED");
break;
case AudioEffect::EVENT_ENABLE_STATUS_CHANGED:
if (info == 0) {
LOGW("EVENT_ENABLE_STATUS_CHANGED info == NULL");
goto effectCallback_Exit;
}
param = *(bool *)info;
arg1 = (int)param;
LOGV("EVENT_ENABLE_STATUS_CHANGED");
break;
case AudioEffect::EVENT_PARAMETER_CHANGED:
if (info == 0) {
LOGW("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) {
LOGE("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;
LOGV("EVENT_PARAMETER_CHANGED");
break;
case AudioEffect::EVENT_ERROR:
LOGW("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();
}
}
// ----------------------------------------------------------------------------
// 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)
{
LOGV("android_media_AudioEffect_native_init");
fields.clazzEffect = NULL;
fields.clazzDesc = NULL;
// Get the AudioEffect class
jclass clazz = env->FindClass(kClassPathName);
if (clazz == NULL) {
LOGE("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) {
LOGE("Can't find AudioEffect.%s", "postEventFromNative");
return;
}
// Get the variables fields
// nativeTrackInJavaObj
fields.fidNativeAudioEffect = env->GetFieldID(
fields.clazzEffect,
"mNativeAudioEffect", "I");
if (fields.fidNativeAudioEffect == NULL) {
LOGE("Can't find AudioEffect.%s", "mNativeAudioEffect");
return;
}
// fidJniData;
fields.fidJniData = env->GetFieldID(
fields.clazzEffect,
"mJniData", "I");
if (fields.fidJniData == NULL) {
LOGE("Can't find AudioEffect.%s", "mJniData");
return;
}
clazz = env->FindClass("android/media/audiofx/AudioEffect$Descriptor");
if (clazz == NULL) {
LOGE("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) {
LOGE("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)
{
LOGV("android_media_AudioEffect_native_setup");
AudioEffectJniStorage* lpJniStorage = NULL;
int lStatus = AUDIOEFFECT_ERROR_NO_MEMORY;
AudioEffect* lpAudioEffect = NULL;
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;
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) {
LOGE("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);
LOGV("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) {
LOGE("setup: NULL java array for id pointer");
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
goto setup_failure;
}
// create the native AudioEffect object
lpAudioEffect = new AudioEffect(typeStr,
uuidStr,
priority,
effectCallback,
&lpJniStorage->mCallbackData,
sessionId,
0);
if (lpAudioEffect == NULL) {
LOGE("Error creating AudioEffect");
goto setup_failure;
}
lStatus = translateError(lpAudioEffect->initCheck());
if (lStatus != AUDIOEFFECT_SUCCESS && lStatus != AUDIOEFFECT_ERROR_ALREADY_EXISTS) {
LOGE("AudioEffect initCheck failed %d", lStatus);
goto setup_failure;
}
nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
if (nId == NULL) {
LOGE("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 {
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) {
LOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
goto setup_failure;
}
env->SetObjectArrayElement(javadesc, 0, jdesc);
env->SetIntField(thiz, fields.fidNativeAudioEffect, (int)lpAudioEffect);
env->SetIntField(thiz, fields.fidJniData, (int)lpJniStorage);
return AUDIOEFFECT_SUCCESS;
// failures:
setup_failure:
if (nId != NULL) {
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
}
if (lpAudioEffect) {
delete lpAudioEffect;
}
env->SetIntField(thiz, fields.fidNativeAudioEffect, 0);
if (lpJniStorage) {
delete lpJniStorage;
}
env->SetIntField(thiz, fields.fidJniData, 0);
if (uuidStr != NULL) {
env->ReleaseStringUTFChars(uuid, uuidStr);
}
if (typeStr != NULL) {
env->ReleaseStringUTFChars(type, typeStr);
}
return lStatus;
}
// ----------------------------------------------------------------------------
static void android_media_AudioEffect_native_finalize(JNIEnv *env, jobject thiz) {
LOGV("android_media_AudioEffect_native_finalize jobject: %x\n", (int)thiz);
// delete the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
thiz, fields.fidNativeAudioEffect);
if (lpAudioEffect) {
LOGV("deleting AudioEffect: %x\n", (int)lpAudioEffect);
delete lpAudioEffect;
}
// delete the JNI data
AudioEffectJniStorage* lpJniStorage = (AudioEffectJniStorage *)env->GetIntField(
thiz, fields.fidJniData);
if (lpJniStorage) {
LOGV("deleting pJniStorage: %x\n", (int)lpJniStorage);
delete lpJniStorage;
}
}
// ----------------------------------------------------------------------------
static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz) {
// do everything a call to finalize would
android_media_AudioEffect_native_finalize(env, thiz);
// + reset the native resources in the Java object so any attempt to access
// them after a call to release fails.
env->SetIntField(thiz, fields.fidNativeAudioEffect, 0);
env->SetIntField(thiz, fields.fidJniData, 0);
}
static jint
android_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
{
// retrieve the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
thiz, fields.fidNativeAudioEffect);
if (lpAudioEffect == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for enable()");
return AUDIOEFFECT_ERROR_NO_INIT;
}
return translateError(lpAudioEffect->setEnabled(enabled));
}
static jboolean
android_media_AudioEffect_native_getEnabled(JNIEnv *env, jobject thiz)
{
// retrieve the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
thiz, fields.fidNativeAudioEffect);
if (lpAudioEffect == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for getEnabled()");
return false;
}
return (jboolean)lpAudioEffect->getEnabled();
}
static jboolean
android_media_AudioEffect_native_hasControl(JNIEnv *env, jobject thiz)
{
// retrieve the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
thiz, fields.fidNativeAudioEffect);
if (lpAudioEffect == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for hasControl()");
return false;
}
if (lpAudioEffect->initCheck() == NO_ERROR) {
return true;
} else {
return false;
}
}
static jint android_media_AudioEffect_native_setParameter(JNIEnv *env,
jobject thiz, int psize, jbyteArray pJavaParam, int 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;
AudioEffect* lpAudioEffect = (AudioEffect *) env->GetIntField(thiz,
fields.fidNativeAudioEffect);
if (lpAudioEffect == NULL) {
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) {
LOGE("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) {
LOGE("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 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;
AudioEffect* lpAudioEffect = (AudioEffect *) env->GetIntField(thiz,
fields.fidNativeAudioEffect);
if (lpAudioEffect == NULL) {
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) {
LOGE("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) {
LOGE("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 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;
// retrieve the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *) env->GetIntField(thiz,
fields.fidNativeAudioEffect);
if (lpAudioEffect == NULL) {
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) {
LOGE("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) {
LOGE("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)
{
effect_descriptor_t desc;
char str[EFFECT_STRING_LEN_MAX];
uint32_t numEffects;
uint32_t i = 0;
jstring jdescType;
jstring jdescUuid;
jstring jdescConnect;
jstring jdescName;
jstring jdescImplementor;
jobject jdesc;
AudioEffect::queryNumberEffects(&numEffects);
jobjectArray ret = env->NewObjectArray(numEffects, fields.clazzDesc, NULL);
if (ret == NULL) {
return ret;
}
LOGV("queryEffects() numEffects: %d", numEffects);
for (i = 0; i < numEffects; 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) {
LOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
goto queryEffects_failure;
}
env->SetObjectArrayElement(ret, i, jdesc);
}
return ret;
queryEffects_failure:
if (ret != NULL) {
env->DeleteLocalRef(ret);
}
return NULL;
}
static jobjectArray
android_media_AudioEffect_native_queryPreProcessings(JNIEnv *env, jclass clazz, jint audioSession)
{
// kDefaultNumEffects is a "reasonable" value ensuring that only one query will be enough on
// most devices to get all active audio pre processing on a given session.
static const uint32_t kDefaultNumEffects = 5;
effect_descriptor_t *descriptors = new effect_descriptor_t[kDefaultNumEffects];
uint32_t numEffects = kDefaultNumEffects;
status_t status = AudioEffect::queryDefaultPreProcessing(audioSession,
descriptors,
&numEffects);
if ((status != NO_ERROR && status != NO_MEMORY) ||
numEffects == 0) {
delete[] descriptors;
return NULL;
}
if (status == NO_MEMORY) {
delete [] descriptors;
descriptors = new effect_descriptor_t[numEffects];
status = AudioEffect::queryDefaultPreProcessing(audioSession,
descriptors,
&numEffects);
}
if (status != NO_ERROR || numEffects == 0) {
delete[] descriptors;
return NULL;
}
LOGV("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) {
LOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
env->DeleteLocalRef(ret);
return NULL;;
}
env->SetObjectArrayElement(ret, i, jdesc);
}
return ret;
}
// ----------------------------------------------------------------------------
// Dalvik VM type signatures
static 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;)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)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed\n");
goto bail;
}
assert(env != NULL);
if (register_android_media_AudioEffect(env) < 0) {
LOGE("ERROR: AudioEffect native registration failed\n");
goto bail;
}
if (register_android_media_visualizer(env) < 0) {
LOGE("ERROR: Visualizer native registration failed\n");
goto bail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
bail:
return result;
}