diff options
author | Eric Laurent <elaurent@google.com> | 2010-09-21 18:18:20 -0700 |
---|---|---|
committer | Eric Laurent <elaurent@google.com> | 2010-09-23 11:04:03 -0700 |
commit | 1a5149e5d7f2dddc8b324f7695e69fd89af73c52 (patch) | |
tree | dfa27fafc0451be6a52857f0aae941767a3de304 /media/java/android/media/audiofx/AudioEffect.java | |
parent | 49a8bbf4eacbf0bde71bab05be0ab40488df109c (diff) | |
download | frameworks_base-1a5149e5d7f2dddc8b324f7695e69fd89af73c52.zip frameworks_base-1a5149e5d7f2dddc8b324f7695e69fd89af73c52.tar.gz frameworks_base-1a5149e5d7f2dddc8b324f7695e69fd89af73c52.tar.bz2 |
Fix issue 3022800.
- Created sub-package android.media.audiofx for audio effects.
- Hid AudioEffect class contructor, methods and variables not needed by
applications
- Added more javadoc comments for AudioEffect Class
Change-Id: Ibbb40d4e714bb248aa32cf82d7763d74efae96d1
Diffstat (limited to 'media/java/android/media/audiofx/AudioEffect.java')
-rw-r--r-- | media/java/android/media/audiofx/AudioEffect.java | 1262 |
1 files changed, 1262 insertions, 0 deletions
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java new file mode 100644 index 0000000..3e54627 --- /dev/null +++ b/media/java/android/media/audiofx/AudioEffect.java @@ -0,0 +1,1262 @@ +/* + * 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.audiofx; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * AudioEffect is the base class for controlling audio effects provided by the android audio + * framework. + * <p>Applications should not use the AudioEffect class directly but one of its derived classes to + * control specific effects: + * <ul> + * <li> {@link android.media.audiofx.Equalizer}</li> + * <li> {@link android.media.audiofx.Virtualizer}</li> + * <li> {@link android.media.audiofx.BassBoost}</li> + * <li> {@link android.media.audiofx.PresetReverb}</li> + * <li> {@link android.media.audiofx.EnvironmentalReverb}</li> + * </ul> + * <p>If the audio effect is to be applied to a specific AudioTrack or MediaPlayer instance, + * the application must specify the audio session ID of that instance when creating the AudioEffect. + * (see {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions). + * To apply an effect to the global audio output mix, session 0 must be specified when creating the + * AudioEffect. + * <p>Creating an effect on the output mix (audio session 0) requires permission + * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} + * <p>Creating an AudioEffect object will create the corresponding effect engine in the audio + * framework if no instance of the same effect type exists in the specified audio session. + * If one exists, this instance will be used. + * <p>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. + */ + +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 + */ + + /** + * UUID for environmental reverb effect + * @hide + */ + public static final UUID EFFECT_TYPE_ENV_REVERB = UUID + .fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e"); + /** + * UUID for preset reverb effect + * @hide + */ + public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID + .fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b"); + /** + * UUID for equalizer effect + * @hide + */ + public static final UUID EFFECT_TYPE_EQUALIZER = UUID + .fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b"); + /** + * UUID for bass boost effect + * @hide + */ + public static final UUID EFFECT_TYPE_BASS_BOOST = UUID + .fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b"); + /** + * UUID for virtualizer effect + * @hide + */ + public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID + .fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b"); + + /** + * Null effect UUID. Used when the UUID for effect type of + * @hide + */ + public static final UUID EFFECT_TYPE_NULL = UUID + .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210"); + + /** + * State of an AudioEffect object that was not successfully initialized upon + * creation + * @hide + */ + public static final int STATE_UNINITIALIZED = 0; + /** + * State of an AudioEffect object that is ready to be used. + * @hide + */ + public static final int STATE_INITIALIZED = 1; + + // to keep in sync with + // frameworks/base/include/media/AudioEffect.h + /** + * Event id for engine control ownership change notification. + * @hide + */ + public static final int NATIVE_EVENT_CONTROL_STATUS = 0; + /** + * Event id for engine state change notification. + * @hide + */ + public static final int NATIVE_EVENT_ENABLED_STATUS = 1; + /** + * Event id for engine parameter change notification. + * @hide + */ + public static final int NATIVE_EVENT_PARAMETER_CHANGED = 2; + + /** + * Successful operation. + */ + public static final int SUCCESS = 0; + /** + * Unspecified error. + */ + public static final int ERROR = -1; + /** + * Internal opreation status. Not returned by any method. + */ + public static final int ALREADY_EXISTS = -2; + /** + * Operation failed due to bad object initialization. + */ + public static final int ERROR_NO_INIT = -3; + /** + * Operation failed due to bad parameter value. + */ + public static final int ERROR_BAD_VALUE = -4; + /** + * Operation failed because it was requested in wrong state. + */ + public static final int ERROR_INVALID_OPERATION = -5; + /** + * Operation failed due to lack of memory. + */ + public static final int ERROR_NO_MEMORY = -6; + /** + * Operation failed due to dead remote object. + */ + public static final int ERROR_DEAD_OBJECT = -7; + + /** + * The effect descriptor contains information on a particular effect implemented in the + * audio framework:<br> + * <ul> + * <li>type: UUID corresponding to the OpenSL ES interface implemented by this effect</li> + * <li>uuid: UUID for this particular implementation</li> + * <li>connectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li> + * <li>name: human readable effect name</li> + * <li>implementor: human readable effect implementor name</li> + * </ul> + * The method {@link #queryEffects()} returns an array of Descriptors to facilitate effects + * enumeration. + */ + public static class Descriptor { + + public Descriptor() { + } + + public Descriptor(String type, String uuid, String connectMode, + String name, String implementor) { + this.type = UUID.fromString(type); + this.uuid = UUID.fromString(uuid); + this.connectMode = connectMode; + this.name = name; + this.implementor = implementor; + } + + /** + * Indicates the generic type of the effect (Equalizer, Bass boost ...). The UUID + * corresponds to the OpenSL ES Interface ID for this type of effect. + */ + public UUID type; + /** + * Indicates the particular implementation of the effect in that type. Several effects + * can have the same type but this uuid is unique to a given implementation. + */ + public UUID uuid; + /** + * Indicates if the effect is of insert category {@link #EFFECT_INSERT} or auxiliary + * category {@link #EFFECT_AUXILIARY}. Insert effects (Typically an Equalizer) are applied + * to the entire audio source and usually not shared by several sources. Auxiliary effects + * (typically a reverberator) are applied to part of the signal (wet) and the effect output + * is added to the original signal (dry). + */ + public String connectMode; + /** + * Human readable effect name + */ + public String name; + /** + * Human readable effect implementor name + */ + public String implementor; + }; + + /** + * Effect connection mode is insert. Specifying an audio session ID when creating the effect + * will insert this effect after all players in the same audio session. + */ + public static final String EFFECT_INSERT = "Insert"; + /** + * Effect connection mode is auxiliary. + * <p>Auxiliary effects must be created on session 0 (global output mix). In order for a + * MediaPlayer or AudioTrack to be fed into this effect, they must be explicitely attached to + * this effect and a send level must be specified. + * <p>Use the effect ID returned by {@link #getId()} to designate this particular effect when + * attaching it to the MediaPlayer or AudioTrack. + */ + public static final String EFFECT_AUXILIARY = "Auxiliary"; + + // -------------------------------------------------------------------------- + // Member variables + // -------------------- + /** + * Indicates the state of the AudioEffect instance + */ + private int mState = STATE_UNINITIALIZED; + /** + * Lock to synchronize access to mState + */ + private final Object mStateLock = new Object(); + /** + * System wide unique effect ID + */ + private 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) + */ + private OnEnableStatusChangeListener mEnableStatusChangeListener = null; + /** + * Listener for effect engine control ownership change notifications. + * + * @see #setControlStatusListener(OnControlStatusChangeListener) + */ + private OnControlStatusChangeListener mControlChangeStatusListener = null; + /** + * Listener for effect engine control ownership change notifications. + * + * @see #setParameterListener(OnParameterChangeListener) + */ + private OnParameterChangeListener mParameterChangeListener = null; + /** + * Lock to protect listeners updates against event notifications + * @hide + */ + public final Object mListenerLock = new Object(); + /** + * Handler for events coming from the native code + * @hide + */ + public 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. This + * parameter can be set to {@link #EFFECT_TYPE_NULL} in which + * case only the uuid will be used to select the effect. + * @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 + * {@link #EFFECT_TYPE_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 + * @hide + */ + + 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 ERROR_BAD_VALUE: + throw (new IllegalArgumentException("Effect type: " + type + + " not supported.")); + case ERROR_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 android.media.audiofx.AudioEffect.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 android.media.audiofx.AudioEffect.Descriptor} objects + * + * @throws IllegalStateException + */ + + static public Descriptor[] queryEffects() { + return (Descriptor[]) native_query_effects(); + } + + // -------------------------------------------------------------------------- + // Control methods + // -------------------- + + /** + * Enable or disable the effect. + * Creating an audio effect does not automatically apply this effect on the audio source. It + * creates the resources necessary to process this effect but the audio signal is still bypassed + * through the effect engine. Calling this method will make that the effect is actually applied + * or not to the audio content being played in the corresponding audio session. + * + * @param enabled the requested enable state + * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION} + * or {@link #ERROR_DEAD_OBJECT} in case of failure. + * @throws IllegalStateException + */ + public int setEnabled(boolean enabled) throws IllegalStateException { + checkState("setEnabled()"); + return native_setEnabled(enabled); + } + + /** + * 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 {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE}, + * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or + * {@link #ERROR_DEAD_OBJECT} in case of failure + * @throws IllegalStateException + * @hide + */ + 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[]) + * @hide + */ + 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[]) + * @hide + */ + 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[]) + * @hide + */ + 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[]) + * @hide + */ + public int setParameter(int[] param, int[] value) + throws IllegalStateException { + if (param.length > 2 || value.length > 2) { + return ERROR_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[]) + * @hide + */ + public int setParameter(int[] param, short[] value) + throws IllegalStateException { + if (param.length > 2 || value.length > 2) { + return ERROR_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[]) + * @hide + */ + public int setParameter(int[] param, byte[] value) + throws IllegalStateException { + if (param.length > 2) { + return ERROR_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 {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE}, + * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or + * {@link #ERROR_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 + * @hide + */ + 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[]) + * @hide + */ + 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[]) + * @hide + */ + public int getParameter(int param, int[] value) + throws IllegalStateException { + if (value.length > 2) { + return ERROR_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[]) + * @hide + */ + public int getParameter(int param, short[] value) + throws IllegalStateException { + if (value.length > 2) { + return ERROR_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[]) + * @hide + */ + public int getParameter(int[] param, int[] value) + throws IllegalStateException { + if (param.length > 2 || value.length > 2) { + return ERROR_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[]) + * @hide + */ + public int getParameter(int[] param, short[] value) + throws IllegalStateException { + if (param.length > 2 || value.length > 2) { + return ERROR_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[]) + * @hide + */ + public int getParameter(int[] param, byte[] value) + throws IllegalStateException { + if (param.length > 2) { + return ERROR_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. + * + * @hide + */ + 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 enabled state + * + * @return true if the effect is enabled, false otherwise. + * @throws IllegalStateException + */ + public boolean getEnabled() throws IllegalStateException { + checkState("getEnabled()"); + return native_getEnabled(); + } + + /** + * 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 + * @hide + */ + 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 + // -------------------- + /** + * The OnEnableStatusChangeListener interface defines a method called by the AudioEffect + * when a the enabled state of the effect engine was changed by the controlling application. + */ + public interface OnEnableStatusChangeListener { + /** + * Called on the listener to notify it that the effect engine has been + * enabled or disabled. + * @param effect the effect on which the interface is registered. + * @param enabled new effect state. + */ + void onEnableStatusChange(AudioEffect effect, boolean enabled); + } + + /** + * The OnControlStatusChangeListener interface defines a method called by the AudioEffect + * when a the control of the effect engine is gained or lost by the application + */ + public interface OnControlStatusChangeListener { + /** + * Called on the listener to notify it that the effect engine control + * has been taken or returned. + * @param effect the effect on which the interface is registered. + * @param controlGranted true if the application has been granted control of the effect + * engine, false otherwise. + */ + void onControlStatusChange(AudioEffect effect, boolean controlGranted); + } + + /** + * The OnParameterChangeListener interface defines a method called by the AudioEffect + * when a parameter is changed in the effect engine by the controlling application. + * @hide + */ + public interface OnParameterChangeListener { + /** + * Called on the listener to notify it that a parameter value has changed. + * @param effect the effect on which the interface is registered. + * @param status status of the set parameter operation. + * @param param ID of the modified parameter. + * @param value the new parameter value. + */ + void onParameterChange(AudioEffect effect, int status, byte[] param, + byte[] value); + } + + + // ------------------------------------------------------------------------- + // Audio Effect Control panel intents + // ------------------------------------------------------------------------- + + /** + * Intent to launch an audio effect control panel UI. + * <p>The goal of this intent is to enable separate implementations of music/media player + * applications and audio effect control application or services. + * This will allow platform vendors to offer more advanced control options for standard effects + * or control for platform specific effects. + * <p>The intent carries a number of extras used by the player application to communicate + * necessary pieces of information to the control panel application. + * <p>The calling application must use the + * {@link android.app.Activity#startActivityForResult(Intent, int)} method to launch the + * control panel so that its package name is indicated and used by the control panel + * application to keep track of changes for this particular application. + * <p>The {@link #EXTRA_AUDIO_SESSION} extra will indicate an audio session to which the + * audio effects should be applied. If no audio session is specified, either one of the + * follownig will happen: + * <p>- If an audio session was previously opened by the calling application with + * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intent, the effect changes will + * be applied to that session. + * <p>- If no audio session is opened, the changes will be stored in the package specific + * storage area and applied whenever a new audio session is opened by this application. + * <p>The {@link #EXTRA_CONTENT_TYPE} extra will help the control panel application + * customize both the UI layout and the default audio effect settings if none are already + * stored for the calling application. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL = + "android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL"; + + /** + * Intent to signal to the effect control application or service that a new audio session + * is opened and requires audio effects to be applied. + * <p>This is different from {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} in that no + * UI should be displayed in this case. Music player applications can broadcast this intent + * before starting playback to make sure that any audio effect settings previously selected + * by the user are applied. + * <p>The effect control application receiving this intent will look for previously stored + * settings for the calling application, create all required audio effects and apply the + * effect settings to the specified audio session. + * <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the + * audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory. + * <p>If no stored settings are found for the calling application, default settings for the + * content type indicated by {@link #EXTRA_CONTENT_TYPE} will be applied. The default settings + * for a given content type are platform specific. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION = + "android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION"; + + /** + * Intent to signal to the effect control application or service that an audio session + * is closed and that effects should not be applied anymore. + * <p>The effect control application receiving this intent will delete all effects on + * this session and store current settings in package specific storage. + * <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the + * audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory. + * <p>It is good practice for applications to broadcast this intent when music playback stops + * and/or when exiting to free system resources consumed by audio effect engines. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION = + "android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION"; + + /** + * Contains the ID of the audio session the effects should be applied to. + * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL}, + * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and + * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents. + * <p>The extra value is of type int and is the audio session ID. + * @see android.media.MediaPlayer#getAudioSessionId() for details on audio sessions. + */ + public static final String EXTRA_AUDIO_SESSION = "android.media.extra.AUDIO_SESSION"; + + /** + * Contains the package name of the calling application. + * <p>This extra is for use with {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and + * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents. + * <p>The extra value is a string containing the full package name. + */ + public static final String EXTRA_PACKAGE_NAME = "android.media.extra.PACKAGE_NAME"; + + /** + * Indicates which type of content is played by the application. + * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} and + * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intents. + * <p>This information is used by the effect control application to customize UI and select + * appropriate default effect settings. The content type is one of the following: + * <ul> + * <li>{@link #CONTENT_TYPE_MUSIC}</li> + * <li>{@link #CONTENT_TYPE_MOVIE}</li> + * <li>{@link #CONTENT_TYPE_GAME}</li> + * <li>{@link #CONTENT_TYPE_VOICE}</li> + * </ul> + * If omitted, the content type defaults to {@link #CONTENT_TYPE_MUSIC}. + */ + public static final String EXTRA_CONTENT_TYPE = "android.media.extra.CONTENT_TYPE"; + + /** + * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is music + */ + public static final int CONTENT_TYPE_MUSIC = 0; + /** + * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is video or movie + */ + public static final int CONTENT_TYPE_MOVIE = 1; + /** + * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is game audio + */ + public static final int CONTENT_TYPE_GAME = 2; + /** + * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is voice audio + */ + public static final int CONTENT_TYPE_VOICE = 3; + + + // --------------------------------------------------------- + // 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_setEnabled(boolean enabled); + + private native final boolean native_getEnabled(); + + 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 + // ------------------ + + /** + * @hide + */ + public void checkState(String methodName) throws IllegalStateException { + synchronized (mStateLock) { + if (mState != STATE_INITIALIZED) { + throw (new IllegalStateException(methodName + + " called on uninitialized AudioEffect.")); + } + } + } + + /** + * @hide + */ + public void checkStatus(int status) { + switch (status) { + case AudioEffect.SUCCESS: + break; + case AudioEffect.ERROR_BAD_VALUE: + throw (new IllegalArgumentException( + "AudioEffect: bad parameter value")); + case AudioEffect.ERROR_INVALID_OPERATION: + throw (new UnsupportedOperationException( + "AudioEffect: invalid parameter operation")); + default: + throw (new RuntimeException("AudioEffect: set/get parameter error")); + } + } + + /** + * @hide + */ + public int byteArrayToInt(byte[] valueBuf) { + return byteArrayToInt(valueBuf, 0); + + } + + /** + * @hide + */ + public int byteArrayToInt(byte[] valueBuf, int offset) { + ByteBuffer converter = ByteBuffer.wrap(valueBuf); + converter.order(ByteOrder.nativeOrder()); + return converter.getInt(offset); + + } + + /** + * @hide + */ + public byte[] intToByteArray(int value) { + ByteBuffer converter = ByteBuffer.allocate(4); + converter.order(ByteOrder.nativeOrder()); + converter.putInt(value); + return converter.array(); + } + + /** + * @hide + */ + public short byteArrayToShort(byte[] valueBuf) { + return byteArrayToShort(valueBuf, 0); + } + + /** + * @hide + */ + public short byteArrayToShort(byte[] valueBuf, int offset) { + ByteBuffer converter = ByteBuffer.wrap(valueBuf); + converter.order(ByteOrder.nativeOrder()); + return converter.getShort(offset); + + } + + /** + * @hide + */ + public byte[] shortToByteArray(short value) { + ByteBuffer converter = ByteBuffer.allocate(2); + converter.order(ByteOrder.nativeOrder()); + short sValue = (short) value; + converter.putShort(sValue); + return converter.array(); + } + + /** + * @hide + */ + public 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; + } +} |