diff options
author | Eric Laurent <elaurent@google.com> | 2010-07-02 08:12:41 -0700 |
---|---|---|
committer | Eric Laurent <elaurent@google.com> | 2010-07-07 11:00:28 -0700 |
commit | df9b81ced437b11f8a3fcf4ba3ea6af703d121e2 (patch) | |
tree | c80b989df744ea74f62c800a8848813174792cc2 /media/java | |
parent | 31e0ffe8444b70500cac319da084c4c45e62aca2 (diff) | |
download | frameworks_base-df9b81ced437b11f8a3fcf4ba3ea6af703d121e2.zip frameworks_base-df9b81ced437b11f8a3fcf4ba3ea6af703d121e2.tar.gz frameworks_base-df9b81ced437b11f8a3fcf4ba3ea6af703d121e2.tar.bz2 |
Added Visualizer effect.
The visualizer 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 as a waveform or
a frequency representation (FFT).
Removed temporary hack made in MediaPlayer for animated wall papers based on audio visualization (snoop() method.
This commit also includes a change in AudioEffect class:
- the enable()/disable() methods have been replaced bya more standard setEnabled() method.
- some fixes in javadoc
Change-Id: Id092a1340e9e38dae68646ade7be054e3a36980e
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); + } + + } + +} + |