am e0219539
: am 01f7ac64
: Merge "Issue 2667802: [Audio Effect Framework] AudioEffect base class and JNI." into kraken
Merge commit 'e0219539c6c3e60de4a35cd836df3b68c963e9e0' into gingerbread-plus-aosp * commit 'e0219539c6c3e60de4a35cd836df3b68c963e9e0': Issue 2667802: [Audio Effect Framework] AudioEffect base class and JNI.
This commit is contained in:
462
include/media/AudioEffect.h
Normal file
462
include/media/AudioEffect.h
Normal file
@ -0,0 +1,462 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_AUDIOEFFECT_H
|
||||
#define ANDROID_AUDIOEFFECT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <media/IAudioFlinger.h>
|
||||
#include <media/IEffect.h>
|
||||
#include <media/IEffectClient.h>
|
||||
#include <media/EffectApi.h>
|
||||
#include <media/AudioSystem.h>
|
||||
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <binder/IInterface.h>
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class effect_param_cblk_t;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class AudioEffect : public RefBase
|
||||
{
|
||||
public:
|
||||
|
||||
/*
|
||||
* Static methods for effect libraries management.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Loads the effect library which path is given as first argument.
|
||||
* This must be the full path of a dynamic library (.so) implementing one or
|
||||
* more effect engines and exposing the effect library interface described in
|
||||
* EffectApi.h. The function returns a handle on the library for use by
|
||||
* further call to unloadEffectLibrary() to unload the library.
|
||||
*
|
||||
* Parameters:
|
||||
* libPath: full path of the dynamic library file in the file system.
|
||||
* handle: address where to return the library handle
|
||||
*
|
||||
* Returned status (from utils/Errors.h) can be:
|
||||
* NO_ERROR successful operation.
|
||||
* PERMISSION_DENIED could not get AudioFlinger interface or
|
||||
* application does not have permission to configure audio
|
||||
* NO_INIT effect factory not initialized or
|
||||
* library could not be loaded or
|
||||
* library does not implement required functions
|
||||
* BAD_VALUE invalid libPath string or handle
|
||||
*
|
||||
* Returned value:
|
||||
* *handle updated with library handle
|
||||
*/
|
||||
static status_t loadEffectLibrary(const char *libPath, int *handle);
|
||||
|
||||
/*
|
||||
* Unloads the effect library which handle is given as argument.
|
||||
*
|
||||
* Parameters:
|
||||
* handle: library handle
|
||||
*
|
||||
* Returned status (from utils/Errors.h) can be:
|
||||
* NO_ERROR successful operation.
|
||||
* PERMISSION_DENIED could not get AudioFlinger interface or
|
||||
* application does not have permission to configure audio
|
||||
* NO_INIT effect factory not initialized
|
||||
* BAD_VALUE invalid handle
|
||||
*/
|
||||
static status_t unloadEffectLibrary(int handle);
|
||||
|
||||
/*
|
||||
* Static methods for effects enumeration.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Returns the number of effects available. This method together
|
||||
* with EffectQueryNext() is used to enumerate all effects:
|
||||
* The enumeration sequence is:
|
||||
* QueryNumberEffects(&num_effects);
|
||||
* while (num_effects--)
|
||||
* QueryNextEffect();
|
||||
*
|
||||
* Parameters:
|
||||
* pNumEffects: address where the number of effects should be returned.
|
||||
*
|
||||
* Returned status (from utils/Errors.h) can be:
|
||||
* NO_ERROR successful operation.
|
||||
* PERMISSION_DENIED could not get AudioFlinger interface
|
||||
* NO_INIT effect library failed to initialize
|
||||
* BAD_VALUE invalid numEffects pointer
|
||||
*
|
||||
* Returned value
|
||||
* *numEffects: updated with number of effects available
|
||||
*/
|
||||
static status_t queryNumberEffects(uint32_t *numEffects);
|
||||
|
||||
/*
|
||||
* Returns number effect descriptor during effect
|
||||
* enumeration.
|
||||
*
|
||||
* Parameters:
|
||||
* pDescriptor: address where the effect descriptor should be returned.
|
||||
*
|
||||
* Returned status (from utils/Errors.h) can be:
|
||||
* NO_ERROR successful operation.
|
||||
* NAME_NOT_FOUND no more effect available
|
||||
* PERMISSION_DENIED could not get AudioFlinger interface
|
||||
* NO_INIT effect library failed to initialize
|
||||
* BAD_VALUE invalid descriptor pointer
|
||||
* INVALID_OPERATION effect list has changed since last execution of queryNumberEffects()
|
||||
*
|
||||
* Returned value
|
||||
* *descriptor: updated with effect descriptor
|
||||
*/
|
||||
static status_t queryNextEffect(effect_descriptor_t *descriptor);
|
||||
|
||||
|
||||
/*
|
||||
* Returns the descriptor for the specified effect uuid.
|
||||
*
|
||||
* Parameters:
|
||||
* uuid: pointer to effect uuid.
|
||||
* descriptor: address where the effect descriptor should be returned.
|
||||
*
|
||||
* Returned status (from utils/Errors.h) can be:
|
||||
* NO_ERROR successful operation.
|
||||
* PERMISSION_DENIED could not get AudioFlinger interface
|
||||
* NO_INIT effect library failed to initialize
|
||||
* BAD_VALUE invalid uuid or descriptor pointers
|
||||
* NAME_NOT_FOUND no effect with this uuid found
|
||||
*
|
||||
* Returned value
|
||||
* *descriptor updated with effect descriptor
|
||||
*/
|
||||
static status_t getEffectDescriptor(effect_uuid_t *uuid, effect_descriptor_t *descriptor);
|
||||
|
||||
|
||||
/*
|
||||
* Events used by callback function (effect_callback_t).
|
||||
*/
|
||||
enum event_type {
|
||||
EVENT_CONTROL_STATUS_CHANGED = 0,
|
||||
EVENT_ENABLE_STATUS_CHANGED = 1,
|
||||
EVENT_PARAMETER_CHANGED = 2,
|
||||
EVENT_ERROR = 3
|
||||
};
|
||||
|
||||
/* Callback function notifying client application of a change in effect engine state or
|
||||
* configuration.
|
||||
* An effect engine can be shared by several applications but only one has the control
|
||||
* of the engine activity and configuration at a time.
|
||||
* The EVENT_CONTROL_STATUS_CHANGED event is received when an application loses or
|
||||
* retrieves the control of the effect engine. Loss of control happens
|
||||
* if another application requests the use of the engine by creating an AudioEffect for
|
||||
* the same effect type but with a higher priority. Control is returned when the
|
||||
* application having the control deletes its AudioEffect object.
|
||||
* The EVENT_ENABLE_STATUS_CHANGED event is received by all applications not having the
|
||||
* control of the effect engine when the effect is enabled or disabled.
|
||||
* The EVENT_PARAMETER_CHANGED event is received by all applications not having the
|
||||
* control of the effect engine when an effect parameter is changed.
|
||||
* The EVENT_ERROR event is received when the media server process dies.
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* event: type of event notified (see enum AudioEffect::event_type).
|
||||
* user: Pointer to context for use by the callback receiver.
|
||||
* info: Pointer to optional parameter according to event type:
|
||||
* - EVENT_CONTROL_STATUS_CHANGED: boolean indicating if control is granted (true)
|
||||
* or stolen (false).
|
||||
* - EVENT_ENABLE_STATUS_CHANGED: boolean indicating if effect is now enabled (true)
|
||||
* or disabled (false).
|
||||
* - EVENT_PARAMETER_CHANGED: pointer to a effect_param_t structure.
|
||||
* - EVENT_ERROR: status_t indicating the error (DEAD_OBJECT when media server dies).
|
||||
*/
|
||||
|
||||
typedef void (*effect_callback_t)(int32_t event, void* user, void *info);
|
||||
|
||||
|
||||
/* Constructor.
|
||||
* AudioEffect is the base class for creating and controlling an effect engine from
|
||||
* the application process. Creating an AudioEffect object will create the effect engine
|
||||
* in the AudioFlinger if no engine of the specified type exists. If one exists, this engine
|
||||
* will be used. The application creating the AudioEffect object (or a derived class like
|
||||
* Reverb for instance) will either receive control of the effect engine or not, depending
|
||||
* on the priority parameter. If priority is higher than the priority used by the current
|
||||
* effect engine owner, the control will be transfered to the new application. Otherwise
|
||||
* control will remain to the previous application. In this case, the new application will be
|
||||
* notified of changes in effect engine state or control ownership by the effect callback.
|
||||
* After creating the AudioEffect, the application must call the initCheck() method and
|
||||
* check the creation status before trying to control the effect engine (see initCheck()).
|
||||
* If the effect is to be applied to an AudioTrack or MediaPlayer only the application
|
||||
* must specify the audio session ID corresponding to this player.
|
||||
*/
|
||||
|
||||
/* Simple Constructor.
|
||||
*/
|
||||
AudioEffect();
|
||||
|
||||
|
||||
/* Constructor.
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* type: type of effect created: can be null if uuid is specified. This corresponds to
|
||||
* the OpenSL ES interface implemented by this effect.
|
||||
* uuid: Uuid of effect created: can be null if type is specified. This uuid corresponds to
|
||||
* a particular implementation of an effect type.
|
||||
* priority: requested priority for effect control: the priority level corresponds to the
|
||||
* value of priority parameter: negative values indicate lower priorities, positive values
|
||||
* higher priorities, 0 being the normal priority.
|
||||
* cbf: optional callback function (see effect_callback_t)
|
||||
* user: pointer to context for use by the callback receiver.
|
||||
* sessionID: audio session this effect is associated to. If 0, the effect will be global to
|
||||
* the output mix. If not 0, the effect will be applied to all players
|
||||
* (AudioTrack or MediaPLayer) within the same audio session.
|
||||
* output: HAL audio output stream to which this effect must be attached. Leave at 0 for
|
||||
* automatic output selection by AudioFlinger.
|
||||
*/
|
||||
|
||||
AudioEffect(const effect_uuid_t *type,
|
||||
const effect_uuid_t *uuid = NULL,
|
||||
int32_t priority = 0,
|
||||
effect_callback_t cbf = 0,
|
||||
void* user = 0,
|
||||
int sessionId = 0,
|
||||
audio_io_handle_t output = 0
|
||||
);
|
||||
|
||||
/* Constructor.
|
||||
* Same as above but with type and uuid specified by character strings
|
||||
*/
|
||||
AudioEffect(const char *typeStr,
|
||||
const char *uuidStr = NULL,
|
||||
int32_t priority = 0,
|
||||
effect_callback_t cbf = 0,
|
||||
void* user = 0,
|
||||
int sessionId = 0,
|
||||
audio_io_handle_t output = 0
|
||||
);
|
||||
|
||||
/* Terminates the AudioEffect and unregisters it from AudioFlinger.
|
||||
* The effect engine is also destroyed if this AudioEffect was the last controlling
|
||||
* the engine.
|
||||
*/
|
||||
~AudioEffect();
|
||||
|
||||
/* Initialize an uninitialized AudioEffect.
|
||||
* Returned status (from utils/Errors.h) can be:
|
||||
* - NO_ERROR or ALREADY_EXISTS: successful initialization
|
||||
* - INVALID_OPERATION: AudioEffect is already initialized
|
||||
* - BAD_VALUE: invalid parameter
|
||||
* - NO_INIT: audio flinger or audio hardware not initialized
|
||||
* */
|
||||
status_t set(const effect_uuid_t *type,
|
||||
const effect_uuid_t *uuid = NULL,
|
||||
int32_t priority = 0,
|
||||
effect_callback_t cbf = 0,
|
||||
void* user = 0,
|
||||
int sessionId = 0,
|
||||
audio_io_handle_t output = 0
|
||||
);
|
||||
|
||||
/* Result of constructing the AudioEffect. This must be checked
|
||||
* before using any AudioEffect API.
|
||||
* initCheck() can return:
|
||||
* - NO_ERROR: the effect engine is successfully created and the application has control.
|
||||
* - ALREADY_EXISTS: the effect engine is successfully created but the application does not
|
||||
* have control.
|
||||
* - NO_INIT: the effect creation failed.
|
||||
*
|
||||
*/
|
||||
status_t initCheck() const;
|
||||
|
||||
|
||||
/* Returns the unique effect Id for the controlled effect engine. This ID is unique
|
||||
* system wide and is used for instance in the case of auxiliary effects to attach
|
||||
* the effect to an AudioTrack or MediaPlayer.
|
||||
*
|
||||
*/
|
||||
int32_t id() const { return mId; }
|
||||
|
||||
/* Returns a descriptor for the effect (see effect_descriptor_t in EffectApi.h).
|
||||
*/
|
||||
effect_descriptor_t descriptor() const;
|
||||
|
||||
/* Returns effect control priority of this AudioEffect object.
|
||||
*/
|
||||
int32_t priority() const { return mPriority; }
|
||||
|
||||
|
||||
/* Enables the effect engine.
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returned status (from utils/Errors.h) can be:
|
||||
* - NO_ERROR: successful operation
|
||||
* - INVALID_OPERATION: the application does not have control of the effect engine
|
||||
*/
|
||||
status_t enable();
|
||||
|
||||
/* Disables the effect engine.
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returned status (from utils/Errors.h) can be:
|
||||
* - NO_ERROR: successful operation
|
||||
* - INVALID_OPERATION: the application does not have control of the effect engine
|
||||
*/
|
||||
status_t disable();
|
||||
|
||||
bool isEnabled() const;
|
||||
|
||||
/* Sets a parameter value.
|
||||
*
|
||||
* Parameters:
|
||||
* param: pointer to effect_param_t structure containing the parameter
|
||||
* and its value (See EffectApi.h).
|
||||
* Returned status (from utils/Errors.h) can be:
|
||||
* - NO_ERROR: successful operation.
|
||||
* - INVALID_OPERATION: the application does not have control of the effect engine.
|
||||
* - BAD_VALUE: invalid parameter identifier or value.
|
||||
* - DEAD_OBJECT: the effect engine has been deleted.
|
||||
*/
|
||||
status_t setParameter(effect_param_t *param);
|
||||
|
||||
/* Prepare a new parameter value that will be set by next call to
|
||||
* setParameterCommit(). This method can be used to set multiple parameters
|
||||
* in a synchronous manner or to avoid multiple binder calls for each
|
||||
* parameter.
|
||||
*
|
||||
* Parameters:
|
||||
* param: pointer to effect_param_t structure containing the parameter
|
||||
* and its value (See EffectApi.h).
|
||||
*
|
||||
* Returned status (from utils/Errors.h) can be:
|
||||
* - NO_ERROR: successful operation.
|
||||
* - INVALID_OPERATION: the application does not have control of the effect engine.
|
||||
* - NO_MEMORY: no more space available in shared memory used for deferred parameter
|
||||
* setting.
|
||||
*/
|
||||
status_t setParameterDeferred(effect_param_t *param);
|
||||
|
||||
/* Commit all parameter values previously prepared by setParameterDeferred().
|
||||
*
|
||||
* Parameters:
|
||||
* none
|
||||
*
|
||||
* Returned status (from utils/Errors.h) can be:
|
||||
* - NO_ERROR: successful operation.
|
||||
* - INVALID_OPERATION: No new parameter values ready for commit.
|
||||
* - BAD_VALUE: invalid parameter identifier or value: there is no indication
|
||||
* as to which of the parameters caused this error.
|
||||
* - DEAD_OBJECT: the effect engine has been deleted.
|
||||
*/
|
||||
status_t setParameterCommit();
|
||||
|
||||
/* Gets a parameter value.
|
||||
*
|
||||
* Parameters:
|
||||
* param: pointer to effect_param_t structure containing the parameter
|
||||
* and the returned value (See EffectApi.h).
|
||||
*
|
||||
* Returned status (from utils/Errors.h) can be:
|
||||
* - NO_ERROR: successful operation.
|
||||
* - INVALID_OPERATION: the AudioEffect was not successfully initialized.
|
||||
* - BAD_VALUE: invalid parameter identifier.
|
||||
* - DEAD_OBJECT: the effect engine has been deleted.
|
||||
*/
|
||||
status_t getParameter(effect_param_t *param);
|
||||
|
||||
/* Sends a command and receives a response to/from effect engine.
|
||||
* See EffectApi.h for details on effect command() function, valid command codes
|
||||
* and formats.
|
||||
*/
|
||||
status_t command(int32_t cmdCode, int32_t cmdSize, void *cmdData, int32_t *replySize, void *replyData);
|
||||
|
||||
|
||||
/*
|
||||
* Utility functions.
|
||||
*/
|
||||
|
||||
/* Converts the string passed as first argument to the effect_uuid_t
|
||||
* pointed to by second argument
|
||||
*/
|
||||
static status_t stringToGuid(const char *str, effect_uuid_t *guid);
|
||||
/* Converts the effect_uuid_t pointed to by first argument to the
|
||||
* string passed as second argument
|
||||
*/
|
||||
static status_t guidToString(const effect_uuid_t *guid, char *str, size_t maxLen);
|
||||
|
||||
private:
|
||||
|
||||
// Implements the IEffectClient interface
|
||||
class EffectClient : public android::BnEffectClient, public android::IBinder::DeathRecipient
|
||||
{
|
||||
public:
|
||||
|
||||
EffectClient(AudioEffect *effect) : mEffect(effect){}
|
||||
|
||||
// IEffectClient
|
||||
virtual void controlStatusChanged(bool controlGranted) {mEffect->controlStatusChanged(controlGranted);}
|
||||
virtual void enableStatusChanged(bool enabled) {mEffect->enableStatusChanged(enabled);}
|
||||
virtual void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData) {
|
||||
mEffect->commandExecuted(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
|
||||
}
|
||||
|
||||
// IBinder::DeathRecipient
|
||||
virtual void binderDied(const wp<IBinder>& who) {mEffect->binderDied();}
|
||||
|
||||
private:
|
||||
AudioEffect *mEffect;
|
||||
};
|
||||
|
||||
|
||||
friend class EffectClient;
|
||||
|
||||
// IEffectClient
|
||||
void controlStatusChanged(bool controlGranted);
|
||||
void enableStatusChanged(bool enabled);
|
||||
void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData);
|
||||
void binderDied();
|
||||
|
||||
|
||||
sp<IEffect> mIEffect; // IEffect binder interface
|
||||
sp<EffectClient> mIEffectClient; // IEffectClient implementation
|
||||
sp<IMemory> mCblkMemory; // shared memory for deferred parameter setting
|
||||
effect_param_cblk_t* mCblk; // control block for deferred parameter setting
|
||||
int32_t mPriority; // priority for effect control
|
||||
status_t mStatus; // effect status
|
||||
volatile int32_t mEnabled; // enable state
|
||||
effect_callback_t mCbf; // callback function for status, control, parameter changes notifications
|
||||
void* mUserData; // client context for callback function
|
||||
effect_descriptor_t mDescriptor; // effect descriptor
|
||||
int32_t mId; // system wide unique effect engine instance identifier
|
||||
int32_t mSessionId; // audio session ID
|
||||
};
|
||||
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_AUDIOEFFECT_H
|
954
media/java/android/media/AudioEffect.java
Normal file
954
media/java/android/media/AudioEffect.java
Normal file
@ -0,0 +1,954 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.util.Log;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.io.IOException;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* AudioEffect is the base class for implementing audio effect control in Java applications.
|
||||
* Creating an AudioEffect object will create the effect engine in audio framework if no
|
||||
* instance of the same effect type exists in the specified audio session.
|
||||
* If one exists, this instance will be used. The application creating the AudioEffect object
|
||||
* (or a derived class) will either receive control of the effect engine or not depending
|
||||
* on the priority parameter. If priority is higher than the priority used by the current
|
||||
* effect engine owner, the control will be transfered to the new object. Otherwise
|
||||
* control will remain with the previous object. In this case, the new application will be
|
||||
* notified of changes in effect engine state or control ownership by the appropiate listener.
|
||||
* If the effect is to be applied to a specific AudioTrack or MediaPlayer instance,
|
||||
* the application must specify the audio session ID of that instance.
|
||||
*
|
||||
* {@hide Pending API council review}
|
||||
*/
|
||||
public class AudioEffect
|
||||
{
|
||||
static {
|
||||
System.loadLibrary("audioeffect_jni");
|
||||
native_init();
|
||||
}
|
||||
|
||||
private final static String TAG = "AudioEffect-JAVA";
|
||||
|
||||
/**
|
||||
* The following UUIDs define effect types corresponding to standard audio effects
|
||||
* whose implementation and interface conform to the OpenSL ES specification.
|
||||
* The definitions match the corresponding interface IDs in OpenSLES_IID.h
|
||||
*/
|
||||
public static final UUID EFFECT_TYPE_ENV_REVERB = UUID.fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e");
|
||||
public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID.fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b");
|
||||
public static final UUID EFFECT_TYPE_EQUALIZER = UUID.fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b");
|
||||
public static final UUID EFFECT_TYPE_BASS_BOOST = UUID.fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b");
|
||||
public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID.fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b");
|
||||
|
||||
public static final UUID EFFECT_TYPE_INVALID = UUID.fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210");
|
||||
|
||||
/**
|
||||
* State of an AudioEffect object that was not successfully initialized upon creation
|
||||
*/
|
||||
public static final int STATE_UNINITIALIZED = 0;
|
||||
/**
|
||||
* State of an AudioEffect object that is ready to be used.
|
||||
*/
|
||||
public static final int STATE_INITIALIZED = 1;
|
||||
|
||||
/**
|
||||
* Event id for engine state change notification.
|
||||
*/
|
||||
protected static final int NATIVE_EVENT_ENABLED_STATUS = 0;
|
||||
/**
|
||||
* Event id for engine control ownership change notification.
|
||||
*/
|
||||
protected static final int NATIVE_EVENT_CONTROL_STATUS = 1;
|
||||
/**
|
||||
* Event id for engine parameter change notification.
|
||||
*/
|
||||
protected static final int NATIVE_EVENT_PARAMETER_CHANGED = 2;
|
||||
|
||||
|
||||
// to keep in sync with frameworks/base/media/jni/audioeffect/android_media_AudioEffect.cpp
|
||||
public static final int SUCCESS = 0;
|
||||
public static final int ERROR = -1;
|
||||
public static final int ALREADY_EXISTS = -2;
|
||||
public static final int NO_INIT = -3;
|
||||
public static final int BAD_VALUE = -4;
|
||||
public static final int INVALID_OPERATION = -5;
|
||||
public static final int NO_MEMORY = -6;
|
||||
public static final int DEAD_OBJECT = -7;
|
||||
|
||||
|
||||
/**
|
||||
* The effect descriptor contains necessary information to facilitate
|
||||
* effects enumeration:
|
||||
* mType: UUID corresponding to the OpenSL ES interface implemented by this effect
|
||||
* mUuid: UUID for this particular implementation
|
||||
* mConnectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}
|
||||
* mName: human readable effect name
|
||||
* mImplementor: human readable effect implementor name
|
||||
*/
|
||||
public static class Descriptor {
|
||||
|
||||
public Descriptor() {
|
||||
}
|
||||
public Descriptor(String type,
|
||||
String uuid,
|
||||
String connectMode,
|
||||
String name,
|
||||
String implementor) {
|
||||
mType = UUID.fromString(type);
|
||||
mUuid = UUID.fromString(uuid);
|
||||
mConnectMode = connectMode;
|
||||
mName = name;
|
||||
mImplementor = implementor;
|
||||
}
|
||||
|
||||
public UUID mType;
|
||||
public UUID mUuid;
|
||||
public String mConnectMode;
|
||||
public String mName;
|
||||
public String mImplementor;
|
||||
};
|
||||
|
||||
public static final String EFFECT_INSERT = "Insert";
|
||||
public static final String EFFECT_AUXILIARY = "Auxiliary";
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Member variables
|
||||
//--------------------
|
||||
/**
|
||||
* Indicates the state of the AudioEffect instance
|
||||
*/
|
||||
protected int mState = STATE_UNINITIALIZED;
|
||||
/**
|
||||
* Lock to synchronize access to mState
|
||||
*/
|
||||
protected final Object mStateLock = new Object();
|
||||
/**
|
||||
* System wide unique effect ID
|
||||
*/
|
||||
protected int mId;
|
||||
|
||||
// accessed by native methods
|
||||
private int mNativeAudioEffect;
|
||||
private int mJniData;
|
||||
|
||||
/**
|
||||
* Effect descriptor
|
||||
*/
|
||||
private Descriptor mDescriptor;
|
||||
|
||||
/**
|
||||
* Listener for effect engine state change notifications.
|
||||
* @see #setEnableStatusListener(OnEnableStatusChangeListener)
|
||||
*/
|
||||
protected OnEnableStatusChangeListener mEnableStatusChangeListener = null;
|
||||
/**
|
||||
* Listener for effect engine control ownership change notifications.
|
||||
* @see #setControlStatusListener(OnControlStatusChangeListener)
|
||||
*/
|
||||
protected OnControlStatusChangeListener mControlChangeStatusListener = null;
|
||||
/**
|
||||
* Listener for effect engine control ownership change notifications.
|
||||
* @see #setParameterListener(OnParameterChangeListener)
|
||||
*/
|
||||
protected OnParameterChangeListener mParameterChangeListener = null;
|
||||
/**
|
||||
* Lock to protect listeners updates against event notifications
|
||||
*/
|
||||
protected final Object mListenerLock = new Object();
|
||||
/**
|
||||
* Handler for events coming from the native code
|
||||
*/
|
||||
protected NativeEventHandler mNativeEventHandler = null;
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Constructor, Finalize
|
||||
//--------------------
|
||||
/**
|
||||
* Class constructor.
|
||||
* @param type: type of effect engine created. See
|
||||
* {@link #EFFECT_TYPE_ENV_REVERB}, {@link #EFFECT_TYPE_EQUALIZER} ...
|
||||
* Types corresponding to built-in effects are defined by AudioEffect class.
|
||||
* Other types can be specified provided they correspond an existing OpenSL ES
|
||||
* interface ID and the corresponsing effect is available on the platform.
|
||||
* If an unspecified effect type is requested, the constructor with throw the
|
||||
* IllegalArgumentException.
|
||||
* @param uuid: unique identifier of a particular effect implementation. Must be
|
||||
* specified if the caller wants to use a particular implementation of an effect type.
|
||||
* This parameter can be set to null in which case only the type will be used to select
|
||||
* the effect.
|
||||
* @param priority: the priority level requested by the application for controlling
|
||||
* the effect engine. As the same effect engine can be shared by several applications,
|
||||
* this parameter indicates how much the requesting application needs control of
|
||||
* effect parameters. The normal priority is 0, above normal is a positive number,
|
||||
* below normal a negative number.
|
||||
* @param audioSession: System wide unique audio session identifier. If audioSession
|
||||
* is not 0, the effect will be attached to the MediaPlayer or AudioTrack in the
|
||||
* same audio session. Otherwise, the effect will apply to the output mix.
|
||||
*
|
||||
* @throws java.lang.IllegalArgumentException
|
||||
* @throws java.lang.UnsupportedOperationException
|
||||
* @throws java.lang.RuntimeException
|
||||
*/
|
||||
|
||||
public AudioEffect(UUID type, UUID uuid, int priority, int audioSession)
|
||||
throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
|
||||
int[] id = new int[1];
|
||||
Descriptor[] desc = new Descriptor[1];
|
||||
// native initialization
|
||||
int initResult = native_setup(new WeakReference<AudioEffect>(this),
|
||||
type.toString(), uuid.toString(), priority, audioSession, id, desc);
|
||||
if (initResult != SUCCESS && initResult != ALREADY_EXISTS) {
|
||||
Log.e(TAG, "Error code "+initResult+" when initializing AudioEffect.");
|
||||
switch (initResult) {
|
||||
case BAD_VALUE:
|
||||
throw (new IllegalArgumentException("Effect type: "+type+ " not supported."));
|
||||
case INVALID_OPERATION:
|
||||
throw (new UnsupportedOperationException("Effect library not loaded"));
|
||||
default:
|
||||
throw (new RuntimeException("Cannot initialize effect engine for type: "+type+
|
||||
"Error: "+ initResult));
|
||||
}
|
||||
}
|
||||
mId = id[0];
|
||||
mDescriptor = desc[0];
|
||||
synchronized (mStateLock) {
|
||||
mState = STATE_INITIALIZED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the native AudioEffect resources. It is a good practice to release the
|
||||
* effect engine when not in use as control can be returned to other applications
|
||||
* or the native resources released.
|
||||
*/
|
||||
public void release() {
|
||||
synchronized (mStateLock) {
|
||||
native_release();
|
||||
mState = STATE_UNINITIALIZED;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
native_finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the effect descriptor.
|
||||
* {@see #Descriptor}.
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public Descriptor getDescriptor()
|
||||
throws IllegalStateException {
|
||||
checkState("getDescriptor()");
|
||||
return mDescriptor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Effects Enumeration
|
||||
//--------------------
|
||||
|
||||
/**
|
||||
* Query all effects available on the platform. Returns an array of
|
||||
* {@link #Descriptor} objects
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
|
||||
static public Descriptor[] queryEffects() {
|
||||
return (Descriptor[])native_query_effects();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Control methods
|
||||
//--------------------
|
||||
|
||||
/**
|
||||
* Enable effect engine.
|
||||
* @return {@link #NO_ERROR} in case of success,
|
||||
* {@link #INVALID_OPERATION} or {@link #DEAD_OBJECT} in case of failure.
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public int enable()
|
||||
throws IllegalStateException {
|
||||
checkState("enable()");
|
||||
return native_enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable effect engine.
|
||||
* @return NO_ERROR in case of success,
|
||||
* INVALID_OPERATION or DEAD_OBJECT in case of failure.
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public int disable()
|
||||
throws IllegalStateException {
|
||||
checkState("disable()");
|
||||
return native_disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set effect parameter. The setParameter method is provided in several
|
||||
* forms addressing most common parameter formats. This form is the
|
||||
* most generic one where the parameter and its value are both specified
|
||||
* as an array of bytes. The parameter and value type and length are therefore
|
||||
* totally free. For standard effect defined by OpenSL ES, the parameter format
|
||||
* and values must match the definitions in the corresponding OpenSL ES interface.
|
||||
*
|
||||
* @param param: the identifier of the parameter to set
|
||||
* @param value: the new value for the specified parameter
|
||||
* @return NO_ERROR in case of success,
|
||||
* {@link #BAD_VALUE}, {@link #NO_MEMORY}, {@link #INVALID_OPERATION} or {@link DEAD_OBJECT} in case of failure
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public int setParameter(byte[] param, byte[] value)
|
||||
throws IllegalStateException {
|
||||
checkState("setParameter()");
|
||||
return native_setParameter(param.length, param, value.length, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set effect parameter. The parameter and its value are integers.
|
||||
* @see #setParameter(byte[], byte[])
|
||||
*/
|
||||
public int setParameter(int param, int value)
|
||||
throws IllegalStateException {
|
||||
byte[] p = intToByteArray(param);
|
||||
byte[] v = intToByteArray(value);
|
||||
return setParameter(p, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set effect parameter. The parameter is an integer and the value is a short integer.
|
||||
* @see #setParameter(byte[], byte[])
|
||||
*/
|
||||
public int setParameter(int param, short value)
|
||||
throws IllegalStateException {
|
||||
byte[] p = intToByteArray(param);
|
||||
byte[] v = shortToByteArray(value);
|
||||
return setParameter(p, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set effect parameter. The parameter is an integer and the value is an array of bytes.
|
||||
* @see #setParameter(byte[], byte[])
|
||||
*/
|
||||
public int setParameter(int param, byte[] value)
|
||||
throws IllegalStateException {
|
||||
byte[] p = intToByteArray(param);
|
||||
return setParameter(p, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set effect parameter. The parameter is an array of 1 or 2 integers and the value
|
||||
* is also an array of 1 or 2 integers
|
||||
* @see #setParameter(byte[], byte[])
|
||||
*/
|
||||
public int setParameter(int[] param, int[] value)
|
||||
throws IllegalStateException {
|
||||
if (param.length > 2 || value.length > 2) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
byte[] p = intToByteArray(param[0]);
|
||||
if (param.length > 1) {
|
||||
byte[] p2 = intToByteArray(param[1]);
|
||||
p = concatArrays(p, p2);
|
||||
}
|
||||
byte[] v = intToByteArray(value[0]);
|
||||
if (value.length > 1) {
|
||||
byte[] v2 = intToByteArray(value[1]);
|
||||
v = concatArrays(v, v2);
|
||||
}
|
||||
return setParameter(p, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set effect parameter. The parameter is an array of 1 or 2 integers and the value
|
||||
* is an array of 1 or 2 short integers
|
||||
* @see #setParameter(byte[], byte[])
|
||||
*/
|
||||
public int setParameter(int[] param, short[] value)
|
||||
throws IllegalStateException {
|
||||
if (param.length > 2 || value.length > 2) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
byte[] p = intToByteArray(param[0]);
|
||||
if (param.length > 1) {
|
||||
byte[] p2 = intToByteArray(param[1]);
|
||||
p = concatArrays(p, p2);
|
||||
}
|
||||
|
||||
byte[] v = shortToByteArray(value[0]);
|
||||
if (value.length > 1) {
|
||||
byte[] v2 = shortToByteArray(value[1]);
|
||||
v = concatArrays(v, v2);
|
||||
}
|
||||
return setParameter(p, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set effect parameter. The parameter is an array of 1 or 2 integers and the value
|
||||
* is an array of bytes
|
||||
* @see #setParameter(byte[], byte[])
|
||||
*/
|
||||
public int setParameter(int[] param, byte[] value)
|
||||
throws IllegalStateException {
|
||||
if (param.length > 2) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
byte[] p = intToByteArray(param[0]);
|
||||
if (param.length > 1) {
|
||||
byte[] p2 = intToByteArray(param[1]);
|
||||
p = concatArrays(p, p2);
|
||||
}
|
||||
return setParameter(p, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get effect parameter. The getParameter method is provided in several
|
||||
* forms addressing most common parameter formats. This form is the
|
||||
* most generic one where the parameter and its value are both specified
|
||||
* as an array of bytes. The parameter and value type and length are therefore
|
||||
* totally free.
|
||||
* @param param: the identifier of the parameter to set
|
||||
* @param value: the new value for the specified parameter
|
||||
* @return NO_ERROR in case of success,
|
||||
* {@link #BAD_VALUE}, {@link #NO_MEMORY}, {@link #INVALID_OPERATION} or {@link DEAD_OBJECT} in case of failure
|
||||
* When called, value.length indicates the maximum size of the returned parameters value.
|
||||
* When returning, value.length is updated with the actual size of the returned value.
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public int getParameter(byte[] param, byte[] value)
|
||||
throws IllegalStateException {
|
||||
checkState("getParameter()");
|
||||
int[] vSize = new int[1];
|
||||
vSize[0] = value.length;
|
||||
int status = native_getParameter(param.length, param, vSize, value);
|
||||
if (value.length > vSize[0]) {
|
||||
byte[] resizedValue = new byte[vSize[0]];
|
||||
System.arraycopy(value, 0, resizedValue, 0, vSize[0]);
|
||||
value = resizedValue;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get effect parameter. The parameter is an integer and the value is an array of bytes.
|
||||
* @see #getParameter(byte[], byte[])
|
||||
*/
|
||||
public int getParameter(int param, byte[] value)
|
||||
throws IllegalStateException {
|
||||
byte[] p = intToByteArray(param);
|
||||
|
||||
return getParameter(p, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get effect parameter. The parameter is an integer and the value
|
||||
* is an array of 1 or 2 integers
|
||||
* @see #getParameter(byte[], byte[])
|
||||
*/
|
||||
public int getParameter(int param, int[] value)
|
||||
throws IllegalStateException {
|
||||
if (value.length > 2) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
byte[] p = intToByteArray(param);
|
||||
|
||||
byte[] v = new byte[value.length * 4];
|
||||
|
||||
int status = getParameter(p, v);
|
||||
|
||||
value[0] = byteArrayToInt(v);
|
||||
if (v.length > 4) {
|
||||
value[1] = byteArrayToInt(v, 4);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get effect parameter. The parameter is an integer and the value
|
||||
* is an array of 1 or 2 short integers
|
||||
* @see #getParameter(byte[], byte[])
|
||||
*/
|
||||
public int getParameter(int param, short[] value)
|
||||
throws IllegalStateException {
|
||||
if (value.length > 2) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
byte[] p = intToByteArray(param);
|
||||
|
||||
byte[] v = new byte[value.length * 2];
|
||||
|
||||
int status = getParameter(p, v);
|
||||
|
||||
value[0] = byteArrayToShort(v);
|
||||
if (v.length > 2) {
|
||||
value[1] = byteArrayToShort(v, 2);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get effect parameter. The parameter is an array of 1 or 2 integers and the value
|
||||
* is also an array of 1 or 2 integers
|
||||
* @see #getParameter(byte[], byte[])
|
||||
*/
|
||||
public int getParameter(int[] param, int[] value)
|
||||
throws IllegalStateException {
|
||||
if (param.length > 2 || value.length > 2) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
byte[] p = intToByteArray(param[0]);
|
||||
if (param.length > 1) {
|
||||
byte[] p2 = intToByteArray(param[1]);
|
||||
p = concatArrays(p, p2);
|
||||
}
|
||||
byte[] v = new byte[value.length * 4];
|
||||
|
||||
int status = getParameter(p, v);
|
||||
|
||||
value[0] = byteArrayToInt(v);
|
||||
if (v.length > 4) {
|
||||
value[1] = byteArrayToInt(v, 4);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get effect parameter. The parameter is an array of 1 or 2 integers and the value
|
||||
* is an array of 1 or 2 short integers
|
||||
* @see #getParameter(byte[], byte[])
|
||||
*/
|
||||
public int getParameter(int[] param, short[] value)
|
||||
throws IllegalStateException {
|
||||
if (param.length > 2 || value.length > 2) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
byte[] p = intToByteArray(param[0]);
|
||||
if (param.length > 1) {
|
||||
byte[] p2 = intToByteArray(param[1]);
|
||||
p = concatArrays(p, p2);
|
||||
}
|
||||
byte[] v = new byte[value.length * 2];
|
||||
|
||||
int status = getParameter(p, v);
|
||||
|
||||
value[0] = byteArrayToShort(v);
|
||||
if (v.length > 2) {
|
||||
value[1] = byteArrayToShort(v, 2);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get effect parameter. The parameter is an array of 1 or 2 integers and the value
|
||||
* is an array of bytes
|
||||
* @see #getParameter(byte[], byte[])
|
||||
*/
|
||||
public int getParameter(int[] param, byte[] value)
|
||||
throws IllegalStateException {
|
||||
if (param.length > 2) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
byte[] p = intToByteArray(param[0]);
|
||||
if (param.length > 1) {
|
||||
byte[] p2 = intToByteArray(param[1]);
|
||||
p = concatArrays(p, p2);
|
||||
}
|
||||
|
||||
return getParameter(p, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send a command to the effect engine. This method is intended to send proprietary
|
||||
* commands to a particular effect implementation.
|
||||
*
|
||||
*/
|
||||
public int command(int cmdCode, byte[] command, byte[] reply)
|
||||
throws IllegalStateException {
|
||||
checkState("command()");
|
||||
int[] replySize = new int[1];
|
||||
replySize[0] = reply.length;
|
||||
|
||||
int status = native_command(cmdCode, command.length, command, replySize, reply);
|
||||
|
||||
if (reply.length > replySize[0]) {
|
||||
byte[] resizedReply = new byte[replySize[0]];
|
||||
System.arraycopy(reply, 0, resizedReply, 0, replySize[0]);
|
||||
reply = resizedReply;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Getters
|
||||
//--------------------
|
||||
|
||||
/**
|
||||
* Returns effect unique identifier. This system wide unique identifier
|
||||
* can be used to attach this effect to a MediaPlayer or an AudioTrack
|
||||
* when the effect is an auxiliary effect (Reverb)
|
||||
* @return the effect identifier.
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public int getId()
|
||||
throws IllegalStateException {
|
||||
checkState("getId()");
|
||||
return mId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns effect engine enable state
|
||||
* @return true if the effect is enabled, false otherwise.
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public boolean getEnable()
|
||||
throws IllegalStateException {
|
||||
checkState("getEnable()");
|
||||
return native_getEnable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this AudioEffect object is controlling the effect engine.
|
||||
* @return true if this instance has control of effect engine, false otherwise.
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public boolean hasControl()
|
||||
throws IllegalStateException {
|
||||
checkState("hasControl()");
|
||||
return native_hasControl();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Initialization / configuration
|
||||
//--------------------
|
||||
/**
|
||||
* Sets the listener AudioEffect notifies when the effect engine is enabled
|
||||
* or disabled.
|
||||
* @param listener
|
||||
*/
|
||||
public void setEnableStatusListener(OnEnableStatusChangeListener listener) {
|
||||
synchronized (mListenerLock) {
|
||||
mEnableStatusChangeListener = listener;
|
||||
}
|
||||
if ((listener != null) && (mNativeEventHandler == null)) {
|
||||
createNativeEventHandler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener AudioEffect notifies when the effect engine control
|
||||
* is taken or returned.
|
||||
* @param listener
|
||||
*/
|
||||
public void setControlStatusListener(OnControlStatusChangeListener listener) {
|
||||
synchronized (mListenerLock) {
|
||||
mControlChangeStatusListener = listener;
|
||||
}
|
||||
if ((listener != null) && (mNativeEventHandler == null)) {
|
||||
createNativeEventHandler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener AudioEffect notifies when a parameter is changed.
|
||||
* @param listener
|
||||
*/
|
||||
public void setParameterListener(OnParameterChangeListener listener) {
|
||||
synchronized (mListenerLock) {
|
||||
mParameterChangeListener = listener;
|
||||
}
|
||||
if ((listener != null) && (mNativeEventHandler == null)) {
|
||||
createNativeEventHandler();
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience method for the creation of the native event handler
|
||||
// It is called only when a non-null event listener is set.
|
||||
// precondition:
|
||||
// mNativeEventHandler is null
|
||||
private void createNativeEventHandler() {
|
||||
Looper looper;
|
||||
if ((looper = Looper.myLooper()) != null) {
|
||||
mNativeEventHandler = new NativeEventHandler(this, looper);
|
||||
} else if ((looper = Looper.getMainLooper()) != null) {
|
||||
mNativeEventHandler = new NativeEventHandler(this, looper);
|
||||
} else {
|
||||
mNativeEventHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Interface definitions
|
||||
//--------------------
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when the
|
||||
* effect engine is enabled or disabled.
|
||||
*/
|
||||
public interface OnEnableStatusChangeListener {
|
||||
/**
|
||||
* Called on the listener to notify it that the effect engine
|
||||
* has been enabled or disabled.
|
||||
*/
|
||||
void onEnableStatusChange(AudioEffect effect, boolean enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when the
|
||||
* effect engine control is taken or returned.
|
||||
*/
|
||||
public interface OnControlStatusChangeListener {
|
||||
/**
|
||||
* Called on the listener to notify it that the effect engine
|
||||
* control has been taken or returned.
|
||||
*/
|
||||
void onControlStatusChange(AudioEffect effect, boolean controlGranted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when a
|
||||
* parameter value has changed.
|
||||
*/
|
||||
public interface OnParameterChangeListener {
|
||||
/**
|
||||
* Called on the listener to notify it that a parameter value has changed.
|
||||
*/
|
||||
void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Inner classes
|
||||
//--------------------
|
||||
/**
|
||||
* Helper class to handle the forwarding of native events to the appropriate listeners
|
||||
*/
|
||||
private class NativeEventHandler extends Handler
|
||||
{
|
||||
private AudioEffect mAudioEffect;
|
||||
|
||||
public NativeEventHandler(AudioEffect ae, Looper looper) {
|
||||
super(looper);
|
||||
mAudioEffect = ae;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (mAudioEffect == null) {
|
||||
return;
|
||||
}
|
||||
switch(msg.what) {
|
||||
case NATIVE_EVENT_ENABLED_STATUS:
|
||||
OnEnableStatusChangeListener enableStatusChangeListener = null;
|
||||
synchronized (mListenerLock) {
|
||||
enableStatusChangeListener = mAudioEffect.mEnableStatusChangeListener;
|
||||
}
|
||||
if (enableStatusChangeListener != null) {
|
||||
enableStatusChangeListener.onEnableStatusChange(mAudioEffect, (boolean)(msg.arg1 != 0));
|
||||
}
|
||||
break;
|
||||
case NATIVE_EVENT_CONTROL_STATUS:
|
||||
OnControlStatusChangeListener controlStatusChangeListener = null;
|
||||
synchronized (mListenerLock) {
|
||||
controlStatusChangeListener = mAudioEffect.mControlChangeStatusListener;
|
||||
}
|
||||
if (controlStatusChangeListener != null) {
|
||||
controlStatusChangeListener.onControlStatusChange(mAudioEffect, (boolean)(msg.arg1 != 0));
|
||||
}
|
||||
break;
|
||||
case NATIVE_EVENT_PARAMETER_CHANGED:
|
||||
OnParameterChangeListener parameterChangeListener = null;
|
||||
synchronized (mListenerLock) {
|
||||
parameterChangeListener = mAudioEffect.mParameterChangeListener;
|
||||
}
|
||||
if (parameterChangeListener != null) {
|
||||
// arg1 contains offset of parameter value from start of byte array
|
||||
int vOffset = msg.arg1;
|
||||
byte[] p = (byte[])msg.obj;
|
||||
// See effect_param_t in EffectApi.h for psize and vsize fields offsets
|
||||
int status = byteArrayToInt(p, 0);
|
||||
int psize = byteArrayToInt(p, 4);
|
||||
int vsize = byteArrayToInt(p, 8);
|
||||
byte[] param = new byte[psize];
|
||||
byte[] value = new byte[vsize];
|
||||
System.arraycopy(p, 12, param, 0, psize);
|
||||
System.arraycopy(p, vOffset, value, 0, vsize);
|
||||
|
||||
parameterChangeListener.onParameterChange(mAudioEffect, status, param, value);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Log.e(TAG, "handleMessage() Unknown event type: " + msg.what);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Java methods called from the native side
|
||||
//--------------------
|
||||
@SuppressWarnings("unused")
|
||||
private static void postEventFromNative(Object effect_ref,
|
||||
int what, int arg1, int arg2, Object obj) {
|
||||
AudioEffect effect = (AudioEffect)((WeakReference)effect_ref).get();
|
||||
if (effect == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (effect.mNativeEventHandler != null) {
|
||||
Message m = effect.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj);
|
||||
effect.mNativeEventHandler.sendMessage(m);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Native methods called from the Java side
|
||||
//--------------------
|
||||
|
||||
private static native final void native_init();
|
||||
|
||||
private native final int native_setup(Object audioeffect_this,
|
||||
String type,
|
||||
String uuid,
|
||||
int priority,
|
||||
int audioSession,
|
||||
int[] id,
|
||||
Object[] desc);
|
||||
|
||||
private native final void native_finalize();
|
||||
|
||||
private native final void native_release();
|
||||
|
||||
private native final int native_enable();
|
||||
|
||||
private native final int native_disable();
|
||||
|
||||
private native final boolean native_getEnable();
|
||||
|
||||
private native final boolean native_hasControl();
|
||||
|
||||
private native final int native_setParameter(int psize,
|
||||
byte[] param,
|
||||
int vsize,
|
||||
byte[] value);
|
||||
|
||||
private native final int native_getParameter(int psize,
|
||||
byte[] param,
|
||||
int[] vsize,
|
||||
byte[] value);
|
||||
|
||||
private native final int native_command(int cmdCode,
|
||||
int cmdSize,
|
||||
byte[] cmdData,
|
||||
int[] repSize,
|
||||
byte[] repData);
|
||||
|
||||
private static native Object[] native_query_effects();
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Utility methods
|
||||
//------------------
|
||||
|
||||
protected void checkState(String methodName)
|
||||
throws IllegalStateException {
|
||||
synchronized (mStateLock) {
|
||||
if (mState != STATE_INITIALIZED) {
|
||||
throw(new IllegalStateException(methodName+" called on uninitialized AudioEffect."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkStatus(int status) {
|
||||
switch (status) {
|
||||
case AudioEffect.SUCCESS:
|
||||
break;
|
||||
case AudioEffect.BAD_VALUE:
|
||||
throw (new IllegalArgumentException("AudioEffect: bad parameter value"));
|
||||
case AudioEffect.INVALID_OPERATION:
|
||||
throw (new UnsupportedOperationException("AudioEffect: invalid parameter operation"));
|
||||
default:
|
||||
throw (new RuntimeException("AudioEffect: set/get parameter error"));
|
||||
}
|
||||
}
|
||||
|
||||
protected int byteArrayToInt(byte[] valueBuf) {
|
||||
return byteArrayToInt(valueBuf, 0);
|
||||
|
||||
}
|
||||
protected int byteArrayToInt(byte[] valueBuf, int offset) {
|
||||
ByteBuffer converter = ByteBuffer.wrap(valueBuf);
|
||||
converter.order(ByteOrder.nativeOrder());
|
||||
return converter.getInt(offset);
|
||||
|
||||
}
|
||||
|
||||
protected byte[] intToByteArray(int value) {
|
||||
ByteBuffer converter = ByteBuffer.allocate(4);
|
||||
converter.order(ByteOrder.nativeOrder());
|
||||
converter.putInt(value);
|
||||
return converter.array();
|
||||
}
|
||||
|
||||
protected short byteArrayToShort(byte[] valueBuf) {
|
||||
return byteArrayToShort(valueBuf, 0);
|
||||
}
|
||||
|
||||
protected short byteArrayToShort(byte[] valueBuf, int offset) {
|
||||
ByteBuffer converter = ByteBuffer.wrap(valueBuf);
|
||||
converter.order(ByteOrder.nativeOrder());
|
||||
return converter.getShort(offset);
|
||||
|
||||
}
|
||||
|
||||
protected byte[] shortToByteArray(short value) {
|
||||
ByteBuffer converter = ByteBuffer.allocate(2);
|
||||
converter.order(ByteOrder.nativeOrder());
|
||||
short sValue = (short)value;
|
||||
converter.putShort(sValue);
|
||||
return converter.array();
|
||||
}
|
||||
|
||||
protected byte[] concatArrays(byte[] ...arrays) {
|
||||
int len = 0;
|
||||
for (byte[] a : arrays) {
|
||||
len += a.length;
|
||||
}
|
||||
byte[] b = new byte[len];
|
||||
|
||||
int offs = 0;
|
||||
for (byte[] a : arrays) {
|
||||
System.arraycopy(a, 0, b, offs, a.length);
|
||||
offs += a.length;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
@ -65,4 +65,5 @@ LOCAL_MODULE:= libmedia_jni
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
# build libsoundpool.so
|
||||
include $(LOCAL_PATH)/soundpool/Android.mk
|
||||
# build libaudioeffect_jni.so
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||
|
16
media/jni/audioeffect/Android.mk
Normal file
16
media/jni/audioeffect/Android.mk
Normal file
@ -0,0 +1,16 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES:= \
|
||||
android_media_AudioEffect.cpp
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libcutils \
|
||||
libutils \
|
||||
libandroid_runtime \
|
||||
libnativehelper \
|
||||
libmedia
|
||||
|
||||
LOCAL_MODULE:= libaudioeffect_jni
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
861
media/jni/audioeffect/android_media_AudioEffect.cpp
Normal file
861
media/jni/audioeffect/android_media_AudioEffect.cpp
Normal file
@ -0,0 +1,861 @@
|
||||
/*
|
||||
* 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/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/AudioEffect$Descriptor");
|
||||
if (clazz == NULL) {
|
||||
LOGE("Can't find android/media/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/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) {
|
||||
nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
|
||||
if (nId == NULL) {
|
||||
LOGE("setup: Error retrieving id pointer");
|
||||
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
|
||||
goto setup_failure;
|
||||
}
|
||||
} else {
|
||||
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,
|
||||
0,
|
||||
sessionId);
|
||||
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[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_enable(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 enable()");
|
||||
return AUDIOEFFECT_ERROR_NO_INIT;
|
||||
}
|
||||
|
||||
return translateError(lpAudioEffect->enable());
|
||||
}
|
||||
|
||||
|
||||
static jint
|
||||
android_media_AudioEffect_native_disable(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 disable()");
|
||||
return AUDIOEFFECT_ERROR_NO_INIT;
|
||||
}
|
||||
|
||||
return translateError(lpAudioEffect->disable());
|
||||
}
|
||||
|
||||
|
||||
static jboolean
|
||||
android_media_AudioEffect_native_getEnable(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->isEnabled();
|
||||
}
|
||||
|
||||
|
||||
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 getEnabled()");
|
||||
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, psize);
|
||||
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, int psize, jbyteArray pJavaParam,
|
||||
jintArray pJavaValueSize, jbyteArray pJavaValue) {
|
||||
// retrieve the AudioEffect object
|
||||
jbyte* lpParam = NULL;
|
||||
jbyte* lpValue = NULL;
|
||||
jbyte* lpValueSize = 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 || pJavaValueSize == NULL || 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;
|
||||
}
|
||||
|
||||
// get the pointer for the value size from the java array
|
||||
lpValueSize = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValueSize, NULL);
|
||||
if (lpValueSize == NULL) {
|
||||
LOGE("getParameter: Error retrieving value size pointer");
|
||||
goto getParameter_Exit;
|
||||
}
|
||||
|
||||
voffset = ((psize - 1) / sizeof(int) + 1) * sizeof(int);
|
||||
p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset
|
||||
+ lpValueSize[0]);
|
||||
memcpy(p->data, lpParam, psize);
|
||||
p->psize = psize;
|
||||
p->vsize = lpValueSize[0];
|
||||
|
||||
lStatus = lpAudioEffect->getParameter(p);
|
||||
if (lStatus == NO_ERROR) {
|
||||
lStatus = p->status;
|
||||
if (lStatus == NO_ERROR) {
|
||||
memcpy(lpValue, p->data + voffset, p->vsize);
|
||||
lpValueSize[0] = p->vsize;
|
||||
}
|
||||
}
|
||||
|
||||
free(p);
|
||||
|
||||
getParameter_Exit:
|
||||
|
||||
if (lpParam != NULL) {
|
||||
env->ReleasePrimitiveArrayCritical(pJavaParam, lpParam, 0);
|
||||
}
|
||||
if (lpValue != NULL) {
|
||||
env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0);
|
||||
}
|
||||
if (lpValueSize != NULL) {
|
||||
env->ReleasePrimitiveArrayCritical(pJavaValueSize, lpValueSize, 0);
|
||||
}
|
||||
|
||||
return translateError(lStatus);
|
||||
}
|
||||
|
||||
static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz,
|
||||
jint cmdCode, jint cmdSize, jbyteArray jCmdData, jintArray jReplySize,
|
||||
jbyteArray jReplyData) {
|
||||
jbyte* pCmdData = NULL;
|
||||
jbyte* pReplyData = NULL;
|
||||
jint* pReplySize = 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) || (jReplySize != NULL && 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 size from the java array
|
||||
if (jReplySize != NULL) {
|
||||
pReplySize = (jint *) env->GetPrimitiveArrayCritical(jReplySize, NULL);
|
||||
if (pReplySize == NULL) {
|
||||
LOGE("setParameter: Error retrieving reply pointer");
|
||||
goto command_Exit;
|
||||
}
|
||||
}
|
||||
|
||||
// get the pointer for the reply from the java array
|
||||
if (pReplySize != NULL && pReplySize[0] != 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(cmdCode, cmdSize, pCmdData,
|
||||
pReplySize, pReplyData));
|
||||
|
||||
command_Exit:
|
||||
|
||||
if (pCmdData != NULL) {
|
||||
env->ReleasePrimitiveArrayCritical(jCmdData, pCmdData, 0);
|
||||
}
|
||||
if (pReplyData != NULL) {
|
||||
env->ReleasePrimitiveArrayCritical(jReplyData, pReplyData, 0);
|
||||
}
|
||||
if (pReplySize != NULL) {
|
||||
env->ReleasePrimitiveArrayCritical(jReplySize, pReplySize, 0);
|
||||
}
|
||||
|
||||
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::queryNextEffect(&desc) != NO_ERROR) {
|
||||
goto queryEffects_failure;
|
||||
}
|
||||
|
||||
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 queryEffects_failure;
|
||||
}
|
||||
|
||||
env->SetObjectArrayElement(ret, i, jdesc);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
queryEffects_failure:
|
||||
|
||||
if (ret != NULL) {
|
||||
env->DeleteLocalRef(ret);
|
||||
}
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// 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_enable", "()I", (void *)android_media_AudioEffect_native_enable},
|
||||
{"native_disable", "()I", (void *)android_media_AudioEffect_native_disable},
|
||||
{"native_getEnable", "()Z", (void *)android_media_AudioEffect_native_getEnable},
|
||||
{"native_hasControl", "()Z", (void *)android_media_AudioEffect_native_hasControl},
|
||||
{"native_setParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_setParameter},
|
||||
{"native_getParameter", "(I[B[I[B)I", (void *)android_media_AudioEffect_native_getParameter},
|
||||
{"native_command", "(II[B[I[B)I", (void *)android_media_AudioEffect_native_command},
|
||||
{"native_query_effects", "()[Ljava/lang/Object;", (void *)android_media_AudioEffect_native_queryEffects},
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* success -- return valid version number */
|
||||
result = JNI_VERSION_1_4;
|
||||
|
||||
bail:
|
||||
return result;
|
||||
}
|
||||
|
@ -28,7 +28,8 @@ LOCAL_SRC_FILES:= \
|
||||
IMediaDeathNotifier.cpp \
|
||||
MediaProfiles.cpp \
|
||||
IEffect.cpp \
|
||||
IEffectClient.cpp
|
||||
IEffectClient.cpp \
|
||||
AudioEffect.cpp
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libui libcutils libutils libbinder libsonivox libicuuc libexpat libsurfaceflinger_client libcamera_client
|
||||
|
462
media/libmedia/AudioEffect.cpp
Normal file
462
media/libmedia/AudioEffect.cpp
Normal file
@ -0,0 +1,462 @@
|
||||
/*
|
||||
**
|
||||
** Copyright 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.
|
||||
*/
|
||||
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
#define LOG_TAG "AudioEffect"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <private/media/AudioEffectShared.h>
|
||||
#include <media/AudioEffect.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <cutils/atomic.h>
|
||||
#include <binder/IPCThreadState.h>
|
||||
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
AudioEffect::AudioEffect()
|
||||
: mStatus(NO_INIT)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
AudioEffect::AudioEffect(const effect_uuid_t *type,
|
||||
const effect_uuid_t *uuid,
|
||||
int32_t priority,
|
||||
effect_callback_t cbf,
|
||||
void* user,
|
||||
int sessionId,
|
||||
audio_io_handle_t output
|
||||
)
|
||||
: mStatus(NO_INIT)
|
||||
{
|
||||
mStatus = set(type, uuid, priority, cbf, user, output, sessionId);
|
||||
}
|
||||
|
||||
AudioEffect::AudioEffect(const char *typeStr,
|
||||
const char *uuidStr,
|
||||
int32_t priority,
|
||||
effect_callback_t cbf,
|
||||
void* user,
|
||||
int sessionId,
|
||||
audio_io_handle_t output
|
||||
)
|
||||
: mStatus(NO_INIT)
|
||||
{
|
||||
effect_uuid_t type;
|
||||
effect_uuid_t *pType = NULL;
|
||||
effect_uuid_t uuid;
|
||||
effect_uuid_t *pUuid = NULL;
|
||||
|
||||
LOGV("Constructor string\n - type: %s\n - uuid: %s", typeStr, uuidStr);
|
||||
|
||||
if (typeStr != NULL) {
|
||||
if (stringToGuid(typeStr, &type) == NO_ERROR) {
|
||||
pType = &type;
|
||||
}
|
||||
}
|
||||
|
||||
if (uuidStr != NULL) {
|
||||
if (stringToGuid(uuidStr, &uuid) == NO_ERROR) {
|
||||
pUuid = &uuid;
|
||||
}
|
||||
}
|
||||
|
||||
mStatus = set(pType, pUuid, priority, cbf, user, output, sessionId);
|
||||
}
|
||||
|
||||
status_t AudioEffect::set(const effect_uuid_t *type,
|
||||
const effect_uuid_t *uuid,
|
||||
int32_t priority,
|
||||
effect_callback_t cbf,
|
||||
void* user,
|
||||
int sessionId,
|
||||
audio_io_handle_t output)
|
||||
{
|
||||
sp<IEffect> iEffect;
|
||||
sp<IMemory> cblk;
|
||||
int enabled;
|
||||
|
||||
LOGV("set %p mUserData: %p", this, user);
|
||||
|
||||
if (mIEffect != 0) {
|
||||
LOGW("Effect already in use");
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
|
||||
if (audioFlinger == 0) {
|
||||
LOGE("set(): Could not get audioflinger");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
if (type == NULL && uuid == NULL) {
|
||||
LOGW("Must specify at least type or uuid");
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mPriority = priority;
|
||||
mCbf = cbf;
|
||||
mUserData = user;
|
||||
mSessionId = sessionId;
|
||||
|
||||
memset(&mDescriptor, 0, sizeof(effect_descriptor_t));
|
||||
memcpy(&mDescriptor.type, EFFECT_UUID_NULL, sizeof(effect_uuid_t));
|
||||
memcpy(&mDescriptor.uuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t));
|
||||
|
||||
if (type != NULL) {
|
||||
memcpy(&mDescriptor.type, type, sizeof(effect_uuid_t));
|
||||
}
|
||||
if (uuid != NULL) {
|
||||
memcpy(&mDescriptor.uuid, uuid, sizeof(effect_uuid_t));
|
||||
}
|
||||
|
||||
mIEffectClient = new EffectClient(this);
|
||||
|
||||
iEffect = audioFlinger->createEffect(getpid(), (effect_descriptor_t *)&mDescriptor,
|
||||
mIEffectClient, priority, output, mSessionId, &mStatus, &mId, &enabled);
|
||||
|
||||
if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
|
||||
LOGE("set(): AudioFlinger could not create effect, status: %d", mStatus);
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
mEnabled = (volatile int32_t)enabled;
|
||||
|
||||
mIEffect = iEffect;
|
||||
cblk = iEffect->getCblk();
|
||||
if (cblk == 0) {
|
||||
mStatus = NO_INIT;
|
||||
LOGE("Could not get control block");
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
mIEffect = iEffect;
|
||||
mCblkMemory = cblk;
|
||||
mCblk = static_cast<effect_param_cblk_t*>(cblk->pointer());
|
||||
int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
|
||||
mCblk->buffer = (uint8_t *)mCblk + bufOffset;
|
||||
|
||||
iEffect->asBinder()->linkToDeath(mIEffectClient);
|
||||
LOGV("set() %p OK effect: %s id: %d status %d enabled %d, ", this, mDescriptor.name, mId, mStatus, mEnabled);
|
||||
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
|
||||
AudioEffect::~AudioEffect()
|
||||
{
|
||||
LOGV("Destructor %p", this);
|
||||
|
||||
if (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS) {
|
||||
disable();
|
||||
if (mIEffect != NULL) {
|
||||
mIEffect->disconnect();
|
||||
mIEffect->asBinder()->unlinkToDeath(mIEffectClient);
|
||||
}
|
||||
IPCThreadState::self()->flushCommands();
|
||||
}
|
||||
mIEffect.clear();
|
||||
mIEffectClient.clear();
|
||||
mCblkMemory.clear();
|
||||
}
|
||||
|
||||
|
||||
status_t AudioEffect::initCheck() const
|
||||
{
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
effect_descriptor_t AudioEffect::descriptor() const
|
||||
{
|
||||
return mDescriptor;
|
||||
}
|
||||
|
||||
bool AudioEffect::isEnabled() const
|
||||
{
|
||||
return (mEnabled != 0);
|
||||
}
|
||||
|
||||
status_t AudioEffect::enable()
|
||||
{
|
||||
if (mStatus != NO_ERROR) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
LOGV("enable %p", this);
|
||||
|
||||
if (android_atomic_or(1, &mEnabled) == 0) {
|
||||
return mIEffect->enable();
|
||||
}
|
||||
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
status_t AudioEffect::disable()
|
||||
{
|
||||
if (mStatus != NO_ERROR) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
LOGV("disable %p", this);
|
||||
|
||||
if (android_atomic_and(~1, &mEnabled) == 1) {
|
||||
return mIEffect->disable();
|
||||
}
|
||||
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
status_t AudioEffect::command(int32_t cmdCode, int32_t cmdSize, void *cmdData, int32_t *replySize, void *replyData)
|
||||
{
|
||||
if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
return mIEffect->command(cmdCode, cmdSize, cmdData, replySize, replyData);
|
||||
}
|
||||
|
||||
|
||||
status_t AudioEffect::setParameter(effect_param_t *param)
|
||||
{
|
||||
if (mStatus != NO_ERROR) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
if (param == NULL || param->psize == 0 || param->vsize == 0) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
int size = sizeof(int);
|
||||
int psize = ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;
|
||||
|
||||
LOGV("setParameter: param: %d, param2: %d", *(int *)param->data, (param->psize == 8) ? *((int *)param->data + 1): -1);
|
||||
|
||||
return mIEffect->command(EFFECT_CMD_SET_PARAM, sizeof (effect_param_t) + psize, param, &size, ¶m->status);
|
||||
}
|
||||
|
||||
status_t AudioEffect::setParameterDeferred(effect_param_t *param)
|
||||
{
|
||||
if (mStatus != NO_ERROR) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
if (param == NULL || param->psize == 0 || param->vsize == 0) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock _l(mCblk->lock);
|
||||
|
||||
int psize = ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;
|
||||
int size = ((sizeof(effect_param_t) + psize - 1) / sizeof(int) + 1) * sizeof(int);
|
||||
|
||||
if (mCblk->clientIndex + size > EFFECT_PARAM_BUFFER_SIZE) {
|
||||
return NO_MEMORY;
|
||||
}
|
||||
int *p = (int *)(mCblk->buffer + mCblk->clientIndex);
|
||||
*p++ = size;
|
||||
memcpy(p, param, sizeof(effect_param_t) + psize);
|
||||
mCblk->clientIndex += size;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t AudioEffect::setParameterCommit()
|
||||
{
|
||||
if (mStatus != NO_ERROR) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
Mutex::Autolock _l(mCblk->lock);
|
||||
if (mCblk->clientIndex == 0) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
int size = 0;
|
||||
return mIEffect->command(EFFECT_CMD_SET_PARAM_COMMIT, 0, NULL, &size, NULL);
|
||||
}
|
||||
|
||||
status_t AudioEffect::getParameter(effect_param_t *param)
|
||||
{
|
||||
if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) {
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
if (param == NULL || param->psize == 0 || param->vsize == 0) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
LOGV("getParameter: param: %d, param2: %d", *(int *)param->data, (param->psize == 8) ? *((int *)param->data + 1): -1);
|
||||
|
||||
int psize = sizeof(effect_param_t) + ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;
|
||||
|
||||
return mIEffect->command(EFFECT_CMD_GET_PARAM, sizeof(effect_param_t) + param->psize, param, &psize, param);
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
void AudioEffect::binderDied()
|
||||
{
|
||||
LOGW("IEffect died");
|
||||
mStatus = NO_INIT;
|
||||
if (mCbf) {
|
||||
status_t status = DEAD_OBJECT;
|
||||
mCbf(EVENT_ERROR, mUserData, &status);
|
||||
}
|
||||
mIEffect.clear();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
void AudioEffect::controlStatusChanged(bool controlGranted)
|
||||
{
|
||||
LOGV("controlStatusChanged %p control %d callback %p mUserData %p", this, controlGranted, mCbf, mUserData);
|
||||
if (controlGranted) {
|
||||
if (mStatus == ALREADY_EXISTS) {
|
||||
mStatus = NO_ERROR;
|
||||
}
|
||||
} else {
|
||||
if (mStatus == NO_ERROR) {
|
||||
mStatus = ALREADY_EXISTS;
|
||||
}
|
||||
}
|
||||
if (mCbf) {
|
||||
mCbf(EVENT_CONTROL_STATUS_CHANGED, mUserData, &controlGranted);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioEffect::enableStatusChanged(bool enabled)
|
||||
{
|
||||
LOGV("enableStatusChanged %p enabled %d", this, enabled);
|
||||
if (mStatus == ALREADY_EXISTS) {
|
||||
mEnabled = enabled;
|
||||
if (mCbf) {
|
||||
mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioEffect::commandExecuted(int cmdCode, int cmdSize, void *cmdData, int replySize, void *replyData)
|
||||
{
|
||||
if (cmdData == NULL || replyData == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCbf && cmdCode == EFFECT_CMD_SET_PARAM) {
|
||||
effect_param_t *cmd = (effect_param_t *)cmdData;
|
||||
cmd->status = *(int32_t *)replyData;
|
||||
mCbf(EVENT_PARAMETER_CHANGED, mUserData, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
status_t AudioEffect::loadEffectLibrary(const char *libPath, int *handle)
|
||||
{
|
||||
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
|
||||
if (af == 0) return PERMISSION_DENIED;
|
||||
return af->loadEffectLibrary(libPath, handle);
|
||||
}
|
||||
|
||||
status_t AudioEffect::unloadEffectLibrary(int handle)
|
||||
{
|
||||
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
|
||||
if (af == 0) return PERMISSION_DENIED;
|
||||
return af->unloadEffectLibrary(handle);
|
||||
}
|
||||
|
||||
status_t AudioEffect::queryNumberEffects(uint32_t *numEffects)
|
||||
{
|
||||
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
|
||||
if (af == 0) return PERMISSION_DENIED;
|
||||
return af->queryNumberEffects(numEffects);
|
||||
}
|
||||
|
||||
status_t AudioEffect::queryNextEffect(effect_descriptor_t *descriptor)
|
||||
{
|
||||
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
|
||||
if (af == 0) return PERMISSION_DENIED;
|
||||
return af->queryNextEffect(descriptor);
|
||||
}
|
||||
|
||||
status_t AudioEffect::getEffectDescriptor(effect_uuid_t *uuid, effect_descriptor_t *descriptor)
|
||||
{
|
||||
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
|
||||
if (af == 0) return PERMISSION_DENIED;
|
||||
return af->getEffectDescriptor(uuid, descriptor);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
status_t AudioEffect::stringToGuid(const char *str, effect_uuid_t *guid)
|
||||
{
|
||||
if (str == NULL || guid == NULL) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
int tmp[10];
|
||||
|
||||
if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
|
||||
tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
guid->timeLow = (uint32_t)tmp[0];
|
||||
guid->timeMid = (uint16_t)tmp[1];
|
||||
guid->timeHiAndVersion = (uint16_t)tmp[2];
|
||||
guid->clockSeq = (uint16_t)tmp[3];
|
||||
guid->node[0] = (uint8_t)tmp[4];
|
||||
guid->node[1] = (uint8_t)tmp[5];
|
||||
guid->node[2] = (uint8_t)tmp[6];
|
||||
guid->node[3] = (uint8_t)tmp[7];
|
||||
guid->node[4] = (uint8_t)tmp[8];
|
||||
guid->node[5] = (uint8_t)tmp[9];
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t AudioEffect::guidToString(const effect_uuid_t *guid, char *str, size_t maxLen)
|
||||
{
|
||||
if (guid == NULL || str == NULL) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
snprintf(str, maxLen, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
|
||||
guid->timeLow,
|
||||
guid->timeMid,
|
||||
guid->timeHiAndVersion,
|
||||
guid->clockSeq,
|
||||
guid->node[0],
|
||||
guid->node[1],
|
||||
guid->node[2],
|
||||
guid->node[3],
|
||||
guid->node[4],
|
||||
guid->node[5]);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
}; // namespace android
|
||||
|
Reference in New Issue
Block a user