diff options
author | Eric Laurent <elaurent@google.com> | 2010-06-14 09:10:32 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2010-06-14 09:10:32 -0700 |
commit | 7f5ee3b58d3dc07b88b313aae83ae424d131b355 (patch) | |
tree | 884a424a40bd6aa53ff94d78e82b303a6faf07e5 | |
parent | a47078a5b0ac4af7e27846c731c0dedd1a84ca03 (diff) | |
parent | 184a3ff64ba71e7e860b70b9a836c0132b447138 (diff) | |
download | frameworks_base-7f5ee3b58d3dc07b88b313aae83ae424d131b355.zip frameworks_base-7f5ee3b58d3dc07b88b313aae83ae424d131b355.tar.gz frameworks_base-7f5ee3b58d3dc07b88b313aae83ae424d131b355.tar.bz2 |
am 184a3ff6: am e0219539: am 01f7ac64: Merge "Issue 2667802: [Audio Effect Framework] AudioEffect base class and JNI." into kraken
Merge commit '184a3ff64ba71e7e860b70b9a836c0132b447138'
* commit '184a3ff64ba71e7e860b70b9a836c0132b447138':
Issue 2667802: [Audio Effect Framework] AudioEffect base class and JNI.
-rw-r--r-- | include/media/AudioEffect.h | 462 | ||||
-rw-r--r-- | media/java/android/media/AudioEffect.java | 954 | ||||
-rw-r--r-- | media/jni/Android.mk | 3 | ||||
-rw-r--r-- | media/jni/audioeffect/Android.mk | 16 | ||||
-rw-r--r-- | media/jni/audioeffect/android_media_AudioEffect.cpp | 861 | ||||
-rw-r--r-- | media/libmedia/Android.mk | 3 | ||||
-rw-r--r-- | media/libmedia/AudioEffect.cpp | 462 |
7 files changed, 2759 insertions, 2 deletions
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h new file mode 100644 index 0000000..2bdba2d --- /dev/null +++ b/include/media/AudioEffect.h @@ -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 diff --git a/media/java/android/media/AudioEffect.java b/media/java/android/media/AudioEffect.java new file mode 100644 index 0000000..b1b7fed --- /dev/null +++ b/media/java/android/media/AudioEffect.java @@ -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; + } + +} diff --git a/media/jni/Android.mk b/media/jni/Android.mk index a6a25cd..698cece 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -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)) diff --git a/media/jni/audioeffect/Android.mk b/media/jni/audioeffect/Android.mk new file mode 100644 index 0000000..d03b63b --- /dev/null +++ b/media/jni/audioeffect/Android.mk @@ -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) diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp new file mode 100644 index 0000000..de01dd3 --- /dev/null +++ b/media/jni/audioeffect/android_media_AudioEffect.cpp @@ -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; +} + diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 29cd2ee..7908f5d 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -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 diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp new file mode 100644 index 0000000..8648211 --- /dev/null +++ b/media/libmedia/AudioEffect.cpp @@ -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 + |