diff options
Diffstat (limited to 'media/java')
-rw-r--r-- | media/java/android/media/AudioEffect.java | 713 | ||||
-rwxr-xr-x | media/java/android/media/Visualizer.java | 510 |
2 files changed, 913 insertions, 310 deletions
diff --git a/media/java/android/media/AudioEffect.java b/media/java/android/media/AudioEffect.java index b1b7fed..053cc22 100644 --- a/media/java/android/media/AudioEffect.java +++ b/media/java/android/media/AudioEffect.java @@ -27,22 +27,25 @@ 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. + * AudioEffect is the base class for implementing audio effect control in Java + * applications. + * <p>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. + * <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. + * <p>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 when calling the AudioEffect + * constructor. * - * {@hide Pending API council review} + * { @hide Pending API council review } */ -public class AudioEffect -{ +public class AudioEffect { static { System.loadLibrary("audioeffect_jni"); native_init(); @@ -51,31 +54,60 @@ public class AudioEffect 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 + * 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"); + /** + * UUID for environmental reverb effect + */ + public static final UUID EFFECT_TYPE_ENV_REVERB = UUID + .fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e"); + /** + * UUID for preset reverb effect + */ + public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID + .fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b"); + /** + * UUID for equalizer effect + */ + public static final UUID EFFECT_TYPE_EQUALIZER = UUID + .fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b"); + /** + * UUID for bass boost effect + */ + public static final UUID EFFECT_TYPE_BASS_BOOST = UUID + .fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b"); + /** + * UUID for virtualizer effect + */ + public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID + .fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b"); /** - * State of an AudioEffect object that was not successfully initialized upon creation + * Null effect UUID. Used when the UUID for effect type of + */ + 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 */ 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; + public static final int STATE_INITIALIZED = 1; + // to keep in sync with + // frameworks/base/media/jni/audioeffect/android_media_AudioEffect.cpp /** * Event id for engine state change notification. */ - protected static final int NATIVE_EVENT_ENABLED_STATUS = 0; + protected static final int NATIVE_EVENT_ENABLED_STATUS = 0; /** * Event id for engine control ownership change notification. */ @@ -85,56 +117,89 @@ public class AudioEffect */ protected 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 necessary information to facilitate + * effects enumeration:<br> + * <ul> + * <li>mType: UUID corresponding to the OpenSL ES interface implemented by this effect</li> + * <li>mUuid: UUID for this particular implementation</li> + * <li>mConnectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li> + * <li>mName: human readable effect name</li> + * <li>mImplementor: human readable effect implementor name</li> + * </ul> + */ + 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; + }; + + /** + * 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"; - // 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 */ @@ -159,17 +224,20 @@ public class AudioEffect /** * Listener for effect engine state change notifications. - * @see #setEnableStatusListener(OnEnableStatusChangeListener) + * + * @see #setEnableStatusListener(OnEnableStatusChangeListener) */ protected OnEnableStatusChangeListener mEnableStatusChangeListener = null; /** * Listener for effect engine control ownership change notifications. - * @see #setControlStatusListener(OnControlStatusChangeListener) + * + * @see #setControlStatusListener(OnControlStatusChangeListener) */ protected OnControlStatusChangeListener mControlChangeStatusListener = null; /** * Listener for effect engine control ownership change notifications. - * @see #setParameterListener(OnParameterChangeListener) + * + * @see #setParameterListener(OnParameterChangeListener) */ protected OnParameterChangeListener mParameterChangeListener = null; /** @@ -181,32 +249,36 @@ public class AudioEffect */ 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. + * + * @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 @@ -214,22 +286,28 @@ public class AudioEffect */ public AudioEffect(UUID type, UUID uuid, int priority, int audioSession) - throws IllegalArgumentException, UnsupportedOperationException, RuntimeException { + 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); + type.toString(), uuid.toString(), priority, audioSession, id, + desc); if (initResult != SUCCESS && initResult != ALREADY_EXISTS) { - Log.e(TAG, "Error code "+initResult+" when initializing AudioEffect."); + 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")); + 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)); + throw (new RuntimeException( + "Cannot initialize effect engine for type: " + type + + "Error: " + initResult)); } } mId = id[0]; @@ -240,9 +318,9 @@ public class AudioEffect } /** - * 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. + * 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) { @@ -258,119 +336,115 @@ public class AudioEffect /** * Get the effect descriptor. - * {@see #Descriptor}. + * + //TODO when AudioEffect class is unhidden @ see android.media.AudioEffect.Descriptor * @throws IllegalStateException */ - public Descriptor getDescriptor() - 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 + //TODO when AudioEffect class is unhidden: {@ link android.media.AudioEffect.Descriptor} objects * * @throws IllegalStateException */ static public Descriptor[] queryEffects() { - return (Descriptor[])native_query_effects(); + 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. + * Enable or disable effect engine. + * + * @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 disable() - throws IllegalStateException { - checkState("disable()"); - return native_disable(); + 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. + * 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 + * @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 */ public int setParameter(byte[] param, byte[] value) - throws IllegalStateException { + 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[]) + * + * @see #setParameter(byte[], byte[]) */ - public int setParameter(int param, int value) - throws IllegalStateException { + 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[]) + * 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 { + 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[]) + * 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 { + 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[]) + * 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 { + throws IllegalStateException { if (param.length > 2 || value.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param[0]); if (param.length > 1) { @@ -386,14 +460,15 @@ public class AudioEffect } /** - * 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[]) + * 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 { + throws IllegalStateException { if (param.length > 2 || value.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param[0]); if (param.length > 1) { @@ -410,14 +485,15 @@ public class AudioEffect } /** - * 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[]) + * 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 { + throws IllegalStateException { if (param.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param[0]); if (param.length > 1) { @@ -429,20 +505,23 @@ public class AudioEffect /** * 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 + * 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. + * + * @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 */ public int getParameter(byte[] param, byte[] value) - throws IllegalStateException { + throws IllegalStateException { checkState("getParameter()"); int[] vSize = new int[1]; vSize[0] = value.length; @@ -456,25 +535,28 @@ public class AudioEffect } /** - * Get effect parameter. The parameter is an integer and the value is an array of bytes. - * @see #getParameter(byte[], byte[]) + * 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 { + 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[]) + * 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 { + throws IllegalStateException { if (value.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param); @@ -490,14 +572,15 @@ public class AudioEffect } /** - * Get effect parameter. The parameter is an integer and the value - * is an array of 1 or 2 short integers - * @see #getParameter(byte[], byte[]) + * 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 { + throws IllegalStateException { if (value.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param); @@ -513,14 +596,15 @@ public class AudioEffect } /** - * 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[]) + * 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 { + throws IllegalStateException { if (param.length > 2 || value.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param[0]); if (param.length > 1) { @@ -539,14 +623,15 @@ public class AudioEffect } /** - * 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[]) + * 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 { + throws IllegalStateException { if (param.length > 2 || value.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param[0]); if (param.length > 1) { @@ -565,14 +650,15 @@ public class AudioEffect } /** - * 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[]) + * 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 { + throws IllegalStateException { if (param.length > 2) { - return BAD_VALUE; + return ERROR_BAD_VALUE; } byte[] p = intToByteArray(param[0]); if (param.length > 1) { @@ -583,19 +669,19 @@ public class AudioEffect return getParameter(p, value); } - /** - * Send a command to the effect engine. This method is intended to send proprietary - * commands to a particular effect implementation. + * 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 { + throws IllegalStateException { checkState("command()"); int[] replySize = new int[1]; replySize[0] = reply.length; - int status = native_command(cmdCode, command.length, command, replySize, reply); + int status = native_command(cmdCode, command.length, command, + replySize, reply); if (reply.length > replySize[0]) { byte[] resizedReply = new byte[replySize[0]]; @@ -605,51 +691,53 @@ public class AudioEffect 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) + * 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 { + 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(); + 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. + * + * @return true if this instance has control of effect engine, false + * otherwise. * @throws IllegalStateException */ - public boolean hasControl() - 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) { @@ -662,8 +750,9 @@ public class AudioEffect } /** - * Sets the listener AudioEffect notifies when the effect engine control - * is taken or returned. + * Sets the listener AudioEffect notifies when the effect engine control is + * taken or returned. + * * @param listener */ public void setControlStatusListener(OnControlStatusChangeListener listener) { @@ -677,6 +766,7 @@ public class AudioEffect /** * Sets the listener AudioEffect notifies when a parameter is changed. + * * @param listener */ public void setParameterListener(OnParameterChangeListener listener) { @@ -691,7 +781,7 @@ public class AudioEffect // 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 + // mNativeEventHandler is null private void createNativeEventHandler() { Looper looper; if ((looper = Looper.myLooper()) != null) { @@ -703,52 +793,62 @@ public class AudioEffect } } - //--------------------------------------------------------- + // --------------------------------------------------------- // Interface definitions - //-------------------- + // -------------------- /** - * Interface definition for a callback to be invoked when the - * effect engine is enabled or disabled. + * The OnParameterChangeListener 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 { + public interface OnEnableStatusChangeListener { /** - * Called on the listener to notify it that the effect engine - * has been enabled or disabled. + * 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); } /** - * Interface definition for a callback to be invoked when the - * effect engine control is taken or returned. + * 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 { + public interface OnControlStatusChangeListener { /** - * Called on the listener to notify it that the effect engine - * control has been taken or returned. + * 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); } /** - * Interface definition for a callback to be invoked when a - * parameter value has changed. + * The OnParameterChangeListener interface defines a method called by the AudioEffect + * when a parameter is changed in the effect engine by the controlling application. */ - public interface OnParameterChangeListener { + 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); + 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 + * Helper class to handle the forwarding of native events to the appropriate + * listeners */ - private class NativeEventHandler extends Handler - { + private class NativeEventHandler extends Handler { private AudioEffect mAudioEffect; public NativeEventHandler(AudioEffect ae, Looper looper) { @@ -761,14 +861,15 @@ public class AudioEffect if (mAudioEffect == null) { return; } - switch(msg.what) { + 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)); + enableStatusChangeListener.onEnableStatusChange( + mAudioEffect, (boolean) (msg.arg1 != 0)); } break; case NATIVE_EVENT_CONTROL_STATUS: @@ -777,7 +878,8 @@ public class AudioEffect controlStatusChangeListener = mAudioEffect.mControlChangeStatusListener; } if (controlStatusChangeListener != null) { - controlStatusChangeListener.onControlStatusChange(mAudioEffect, (boolean)(msg.arg1 != 0)); + controlStatusChangeListener.onControlStatusChange( + mAudioEffect, (boolean) (msg.arg1 != 0)); } break; case NATIVE_EVENT_PARAMETER_CHANGED: @@ -786,10 +888,12 @@ public class AudioEffect parameterChangeListener = mAudioEffect.mParameterChangeListener; } if (parameterChangeListener != null) { - // arg1 contains offset of parameter value from start of byte array + // 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 + 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); @@ -798,90 +902,76 @@ public class AudioEffect System.arraycopy(p, 12, param, 0, psize); System.arraycopy(p, vOffset, value, 0, vsize); - parameterChangeListener.onParameterChange(mAudioEffect, status, param, value); + parameterChangeListener.onParameterChange(mAudioEffect, + status, param, value); } break; - default: + 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(); + 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); + 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 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_setEnabled(boolean enabled); - private native final int native_disable(); - - private native final boolean native_getEnable(); + 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_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_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 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 { + protected void checkState(String methodName) throws IllegalStateException { synchronized (mStateLock) { if (mState != STATE_INITIALIZED) { - throw(new IllegalStateException(methodName+" called on uninitialized AudioEffect.")); + throw (new IllegalStateException(methodName + + " called on uninitialized AudioEffect.")); } } } @@ -890,10 +980,12 @@ public class AudioEffect 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")); + 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")); } @@ -903,6 +995,7 @@ public class AudioEffect return byteArrayToInt(valueBuf, 0); } + protected int byteArrayToInt(byte[] valueBuf, int offset) { ByteBuffer converter = ByteBuffer.wrap(valueBuf); converter.order(ByteOrder.nativeOrder()); @@ -931,12 +1024,12 @@ public class AudioEffect protected byte[] shortToByteArray(short value) { ByteBuffer converter = ByteBuffer.allocate(2); converter.order(ByteOrder.nativeOrder()); - short sValue = (short)value; + short sValue = (short) value; converter.putShort(sValue); return converter.array(); } - protected byte[] concatArrays(byte[] ...arrays) { + protected byte[] concatArrays(byte[]... arrays) { int len = 0; for (byte[] a : arrays) { len += a.length; diff --git a/media/java/android/media/Visualizer.java b/media/java/android/media/Visualizer.java new file mode 100755 index 0000000..cdd3cdf --- /dev/null +++ b/media/java/android/media/Visualizer.java @@ -0,0 +1,510 @@ +/* + * 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; + +/** + * The Visualizer class enables application to retrieve part of the currently playing audio for + * visualization purpose. It is not an audio recording interface and only returns partial and low + * quality audio content. However, to protect privacy of certain audio data (e.g voice mail) the use + * of the visualizer requires the permission android.permission.RECORD_AUDIO. + * <p>The audio session ID passed to the constructor indicates which audio content should be + * visualized:<br> + * <ul> + * <li>If the session is 0, the audio output mix is visualized</li> + * <li>If the session is not 0, the audio from a particular {@link MediaPlayer} or + * {@link AudioTrack} + * using this audio session is visualized </li> + * </ul> + * <p>Two types of representation of audio content can be captured: <br> + * <ul> + * <li>Waveform data: consecutive 8-bit (unsigned) mono samples by using the + * {@link #getWaveForm(byte[])} method</li> + * <li>Frequency data: 8-bit magnitude FFT by using the {@link #getFft(byte[])} method</li> + * </ul> + * <p>The length of the capture can be retrieved or specified by calling respectively + * {@link #getCaptureSize()} and {@link #setCaptureSize(int)} methods. Note that the size of the FFT + * is half of the specified capture size but both sides of the spectrum are returned yielding in a + * number of bytes equal to the capture size. The capture size must be a power of 2 in the range + * returned by {@link #getCaptureSizeRange()}. + * <p>In addition to the polling capture mode described above with {@link #getWaveForm(byte[])} and + * {@link #getFft(byte[])} methods, a callback mode is also available by installing a listener by + * use of the {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method. + * The rate at which the listener capture method is called as well as the type of data returned is + * specified. + * <p>Before capturing data, the Visualizer must be enabled by calling the + * {@link #setEnabled(boolean)} method. + * When data capture is not needed any more, the Visualizer should be disabled. + * <p>It is good practice to call the {@link #release()} method when the Visualizer is not used + * anymore to free up native resources associated to the Visualizer instance. + * + * {@hide Pending API council review} + */ + +public class Visualizer { + + static { + System.loadLibrary("audioeffect_jni"); + native_init(); + } + + private final static String TAG = "Visualizer-JAVA"; + + /** + * State of a Visualizer object that was not successfully initialized upon creation + */ + public static final int STATE_UNINITIALIZED = 0; + /** + * State of a Visualizer object that is ready to be used. + */ + public static final int STATE_INITIALIZED = 1; + /** + * State of a Visualizer object that is active. + */ + public static final int STATE_ENABLED = 2; + + // to keep in sync with frameworks/base/media/jni/audioeffect/android_media_Visualizer.cpp + protected static final int NATIVE_EVENT_PCM_CAPTURE = 0; + protected static final int NATIVE_EVENT_FFT_CAPTURE = 1; + + // Error codes: + /** + * 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; + + //-------------------------------------------------------------------------- + // Member variables + //-------------------- + /** + * Indicates the state of the Visualizer instance + */ + protected int mState = STATE_UNINITIALIZED; + /** + * Lock to synchronize access to mState + */ + protected final Object mStateLock = new Object(); + /** + * System wide unique Identifier of the visualizer engine used by this Visualizer instance + */ + protected int mId; + + /** + * 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; + /** + * PCM and FFT capture listener registered by client + */ + protected OnDataCaptureListener mCaptureListener = null; + + // accessed by native methods + private int mNativeVisualizer; + private int mJniData; + + //-------------------------------------------------------------------------- + // Constructor, Finalize + //-------------------- + /** + * Class constructor. + * @param audioSession System wide unique audio session identifier. If audioSession + * is not 0, the visualizer will be attached to the MediaPlayer or AudioTrack in the + * same audio session. Otherwise, the Visualizer will apply to the output mix. + * + * @throws java.lang.UnsupportedOperationException + * @throws java.lang.RuntimeException + */ + + public Visualizer(int audioSession) + throws UnsupportedOperationException, RuntimeException { + int[] id = new int[1]; + + synchronized (mStateLock) { + mState = STATE_UNINITIALIZED; + // native initialization + int result = native_setup(new WeakReference<Visualizer>(this), audioSession, id); + if (result != SUCCESS && result != ALREADY_EXISTS) { + Log.e(TAG, "Error code "+result+" when initializing Visualizer."); + switch (result) { + case ERROR_INVALID_OPERATION: + throw (new UnsupportedOperationException("Effect library not loaded")); + default: + throw (new RuntimeException("Cannot initialize Visualizer engine, error: " + +result)); + } + } + mId = id[0]; + if (native_getEnabled()) { + mState = STATE_ENABLED; + } else { + mState = STATE_INITIALIZED; + } + } + } + + /** + * Releases the native Visualizer resources. It is a good practice to release the + * visualization engine when not in use. + */ + public void release() { + synchronized (mStateLock) { + native_release(); + mState = STATE_UNINITIALIZED; + } + } + + @Override + protected void finalize() { + native_finalize(); + } + + /** + * Enable or disable the visualization engine. + * @param enabled 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 { + synchronized (mStateLock) { + if ((enabled && mState != STATE_INITIALIZED) || + (!enabled && mState != STATE_ENABLED)) { + throw(new IllegalStateException("setEnabled() called in wrong state: "+mState)); + } + int status = native_setEnabled(enabled); + if (status == SUCCESS) { + mState = enabled ? STATE_ENABLED : STATE_INITIALIZED; + } + return status; + } + } + + /** + * Get current activation state of the visualizer. + * @return true if the visualizer is active, false otherwise + */ + public boolean getEnabled() + { + synchronized (mStateLock) { + if (mState == STATE_UNINITIALIZED) { + throw(new IllegalStateException("getEnabled() called in wrong state: "+mState)); + } + return native_getEnabled(); + } + } + + /** + * Returns the capture size range. + * @return the mininum capture size is returned in first array element and the maximum in second + * array element. + */ + public static native int[] getCaptureSizeRange(); + + /** + * Returns the maximum capture rate for the callback capture method. This is the maximum value + * for the rate parameter of the + * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method. + * @return the maximum capture rate expressed in milliHertz + */ + public static native int getMaxCaptureRate(); + + /** + * Sets the capture size, i.e. the number of bytes returned by {@link #getWaveForm(byte[])} and + * {@link #getFft(byte[])} methods. The capture size must be a power of 2 in the range returned + * by {@link #getCaptureSizeRange()}. + * This method must not be called when the Visualizer is enabled. + * @param size requested capture size + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR_BAD_VALUE} in case of failure. + * @throws IllegalStateException + */ + public int setCaptureSize(int size) + throws IllegalStateException { + synchronized (mStateLock) { + if (mState != STATE_INITIALIZED) { + throw(new IllegalStateException("setCaptureSize() called in wrong state: "+mState)); + } + return native_setCaptureSize(size); + } + } + + /** + * Returns current capture size. + * @return the capture size in bytes. + */ + public int getCaptureSize() + throws IllegalStateException { + synchronized (mStateLock) { + if (mState == STATE_UNINITIALIZED) { + throw(new IllegalStateException("getCaptureSize() called in wrong state: "+mState)); + } + return native_getCaptureSize(); + } + } + + /** + * Returns the sampling rate of the captured audio. + * @return the sampling rate in milliHertz. + */ + public int getSamplingRate() + throws IllegalStateException { + synchronized (mStateLock) { + if (mState == STATE_UNINITIALIZED) { + throw(new IllegalStateException("getSamplingRate() called in wrong state: "+mState)); + } + return native_getSamplingRate(); + } + } + + /** + * Returns a waveform capture of currently playing audio content. The capture consists in + * a number of consecutive 8-bit (unsigned) mono PCM samples equal to the capture size returned + * by {@link #getCaptureSize()}. + * <p>This method must be called when the Visualizer is enabled. + * @param waveform array of bytes where the waveform should be returned + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} + * in case of failure. + * @throws IllegalStateException + */ + public int getWaveForm(byte[] waveform) + throws IllegalStateException { + synchronized (mStateLock) { + if (mState != STATE_ENABLED) { + throw(new IllegalStateException("getWaveForm() called in wrong state: "+mState)); + } + return native_getWaveForm(waveform); + } + } + /** + * Returns a frequency capture of currently playing audio content. The capture is a 8-bit + * magnitude FFT. Note that the size of the FFT is half of the specified capture size but both + * sides of the spectrum are returned yielding in a number of bytes equal to the capture size. + * {@see #getCaptureSize()}. + * <p>This method must be called when the Visualizer is enabled. + * @param fft array of bytes where the FFT should be returned + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} + * in case of failure. + * @throws IllegalStateException + */ + public int getFft(byte[] fft) + throws IllegalStateException { + synchronized (mStateLock) { + if (mState != STATE_ENABLED) { + throw(new IllegalStateException("getFft() called in wrong state: "+mState)); + } + return native_getFft(fft); + } + } + + //--------------------------------------------------------- + // Interface definitions + //-------------------- + /** + * The OnDataCaptureListener interface defines methods called by the Visualizer to periodically + * update the audio visualization capture. + * The client application can implement this interface and register the listener with the + * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method. + */ + public interface OnDataCaptureListener { + /** + * Method called when a new waveform capture is available. + * @param visualizer Visualizer object on which the listener is registered. + * @param waveform array of bytes containing the waveform representation. + * @param samplingRate sampling rate of the audio visualized. + */ + void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate); + + /** + * Method called when a new frequency capture is available. + * @param visualizer Visualizer object on which the listener is registered. + * @param fft array of bytes containing the frequency representation. + * @param samplingRate sampling rate of the audio visualized. + */ + void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate); + } + + /** + * Registers an OnDataCaptureListener interface and specifies the rate at which the capture + * should be updated as well as the type of capture requested. + * <p>Call this method with a null listener to stop receiving the capture updates. + * @param listener OnDataCaptureListener registered + * @param rate rate in milliHertz at which the capture should be updated + * @param waveform true if a waveform capture is requested: the onWaveFormDataCapture() + * method will be called on the OnDataCaptureListener interface. + * @param fft true if a frequency capture is requested: the onFftDataCapture() method will be + * called on the OnDataCaptureListener interface. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR_NO_INIT} or {@link #ERROR_BAD_VALUE} in case of failure. + */ + public int setDataCaptureListener(OnDataCaptureListener listener, + int rate, boolean waveform, boolean fft) { + synchronized (mListenerLock) { + mCaptureListener = listener; + } + if (listener == null) { + // make sure capture callback is stopped in native code + waveform = false; + fft = false; + } + int status = native_setPeriodicCapture(rate, waveform, fft); + if (status == SUCCESS) { + if ((listener != null) && (mNativeEventHandler == null)) { + 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; + status = ERROR_NO_INIT; + } + } + } + return status; + } + + /** + * Helper class to handle the forwarding of native events to the appropriate listeners + */ + private class NativeEventHandler extends Handler + { + private Visualizer mVisualizer; + + public NativeEventHandler(Visualizer v, Looper looper) { + super(looper); + mVisualizer = v; + } + + @Override + public void handleMessage(Message msg) { + if (mVisualizer == null) { + return; + } + OnDataCaptureListener l = null; + synchronized (mListenerLock) { + l = mVisualizer.mCaptureListener; + } + + if (l != null) { + byte[] data = (byte[])msg.obj; + int samplingRate = msg.arg1; + switch(msg.what) { + case NATIVE_EVENT_PCM_CAPTURE: + l.onWaveFormDataCapture(mVisualizer, data, samplingRate); + break; + case NATIVE_EVENT_FFT_CAPTURE: + l.onFftDataCapture(mVisualizer, data, samplingRate); + break; + default: + Log.e(TAG,"Unknown native event: "+msg.what); + break; + } + } + } + } + + //--------------------------------------------------------- + // Interface definitions + //-------------------- + + private static native final void native_init(); + + private native final int native_setup(Object audioeffect_this, + int audioSession, + int[] id); + + 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 int native_setCaptureSize(int size); + + private native final int native_getCaptureSize(); + + private native final int native_getSamplingRate(); + + private native final int native_getWaveForm(byte[] waveform); + + private native final int native_getFft(byte[] fft); + + private native final int native_setPeriodicCapture(int rate, boolean waveForm, boolean fft); + + //--------------------------------------------------------- + // Java methods called from the native side + //-------------------- + @SuppressWarnings("unused") + private static void postEventFromNative(Object effect_ref, + int what, int arg1, int arg2, Object obj) { + Visualizer visu = (Visualizer)((WeakReference)effect_ref).get(); + if (visu == null) { + return; + } + + if (visu.mNativeEventHandler != null) { + Message m = visu.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); + visu.mNativeEventHandler.sendMessage(m); + } + + } + +} + |