summaryrefslogtreecommitdiff
path: root/android/media/audiofx
diff options
context:
space:
mode:
authorJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
committerJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
commit10d07c88d69cc64f73a069163e7ea5ba2519a099 (patch)
tree8dbd149eb350320a29c3d10e7ad3201de1c5cbee /android/media/audiofx
parent677516fb6b6f207d373984757d3d9450474b6b00 (diff)
downloadandroid-28-10d07c88d69cc64f73a069163e7ea5ba2519a099.tar.gz
Import Android SDK Platform PI [4335822]
/google/data/ro/projects/android/fetch_artifact \ --bid 4335822 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4335822.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: Ic8f04be005a71c2b9abeaac754d8da8d6f9a2c32
Diffstat (limited to 'android/media/audiofx')
-rw-r--r--android/media/audiofx/AcousticEchoCanceler.java96
-rw-r--r--android/media/audiofx/AudioEffect.java1361
-rw-r--r--android/media/audiofx/AutomaticGainControl.java96
-rw-r--r--android/media/audiofx/BassBoost.java287
-rw-r--r--android/media/audiofx/EnvironmentalReverb.java661
-rw-r--r--android/media/audiofx/Equalizer.java559
-rw-r--r--android/media/audiofx/LoudnessEnhancer.java290
-rw-r--r--android/media/audiofx/NoiseSuppressor.java98
-rw-r--r--android/media/audiofx/PresetReverb.java303
-rw-r--r--android/media/audiofx/Virtualizer.java629
-rw-r--r--android/media/audiofx/Visualizer.java772
11 files changed, 5152 insertions, 0 deletions
diff --git a/android/media/audiofx/AcousticEchoCanceler.java b/android/media/audiofx/AcousticEchoCanceler.java
new file mode 100644
index 00000000..3a44df4d
--- /dev/null
+++ b/android/media/audiofx/AcousticEchoCanceler.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.util.Log;
+
+/**
+ * Acoustic Echo Canceler (AEC).
+ * <p>Acoustic Echo Canceler (AEC) is an audio pre-processor which removes the contribution of the
+ * signal received from the remote party from the captured audio signal.
+ * <p>AEC is used by voice communication applications (voice chat, video conferencing, SIP calls)
+ * where the presence of echo with significant delay in the signal received from the remote party
+ * is highly disturbing. AEC is often used in conjunction with noise suppression (NS).
+ * <p>An application creates an AcousticEchoCanceler object to instantiate and control an AEC
+ * engine in the audio capture path.
+ * <p>To attach the AcousticEchoCanceler to a particular {@link android.media.AudioRecord},
+ * specify the audio session ID of this AudioRecord when creating the AcousticEchoCanceler.
+ * The audio session is retrieved by calling
+ * {@link android.media.AudioRecord#getAudioSessionId()} on the AudioRecord instance.
+ * <p>On some devices, an AEC can be inserted by default in the capture path by the platform
+ * according to the {@link android.media.MediaRecorder.AudioSource} used. The application should
+ * call AcousticEchoCanceler.getEnable() after creating the AEC to check the default AEC activation
+ * state on a particular AudioRecord session.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on
+ * controlling audio effects.
+ */
+
+public class AcousticEchoCanceler extends AudioEffect {
+
+ private final static String TAG = "AcousticEchoCanceler";
+
+ /**
+ * Checks if the device implements acoustic echo cancellation.
+ * @return true if the device implements acoustic echo cancellation, false otherwise.
+ */
+ public static boolean isAvailable() {
+ return AudioEffect.isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_AEC);
+ }
+
+ /**
+ * Creates an AcousticEchoCanceler and attaches it to the AudioRecord on the audio
+ * session specified.
+ * @param audioSession system wide unique audio session identifier. The AcousticEchoCanceler
+ * will be applied to the AudioRecord with the same audio session.
+ * @return AcousticEchoCanceler created or null if the device does not implement AEC.
+ */
+ public static AcousticEchoCanceler create(int audioSession) {
+ AcousticEchoCanceler aec = null;
+ try {
+ aec = new AcousticEchoCanceler(audioSession);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "not implemented on this device"+ aec);
+ } catch (UnsupportedOperationException e) {
+ Log.w(TAG, "not enough resources");
+ } catch (RuntimeException e) {
+ Log.w(TAG, "not enough memory");
+ }
+ return aec;
+ }
+
+ /**
+ * Class constructor.
+ * <p> The constructor is not guarantied to succeed and throws the following exceptions:
+ * <ul>
+ * <li>IllegalArgumentException is thrown if the device does not implement an AEC</li>
+ * <li>UnsupportedOperationException is thrown is the resources allocated to audio
+ * pre-procesing are currently exceeded.</li>
+ * <li>RuntimeException is thrown if a memory allocation error occurs.</li>
+ * </ul>
+ *
+ * @param audioSession system wide unique audio session identifier. The AcousticEchoCanceler
+ * will be applied to the AudioRecord with the same audio session.
+ *
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ private AcousticEchoCanceler(int audioSession)
+ throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_AEC, EFFECT_TYPE_NULL, 0, audioSession);
+ }
+}
diff --git a/android/media/audiofx/AudioEffect.java b/android/media/audiofx/AudioEffect.java
new file mode 100644
index 00000000..7dbca3b9
--- /dev/null
+++ b/android/media/audiofx/AudioEffect.java
@@ -0,0 +1,1361 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.ActivityThread;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import java.lang.ref.WeakReference;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * AudioEffect is the base class for controlling audio effects provided by the android audio
+ * framework.
+ * <p>Applications should not use the AudioEffect class directly but one of its derived classes to
+ * control specific effects:
+ * <ul>
+ * <li> {@link android.media.audiofx.Equalizer}</li>
+ * <li> {@link android.media.audiofx.Virtualizer}</li>
+ * <li> {@link android.media.audiofx.BassBoost}</li>
+ * <li> {@link android.media.audiofx.PresetReverb}</li>
+ * <li> {@link android.media.audiofx.EnvironmentalReverb}</li>
+ * </ul>
+ * <p>To apply the audio effect to a specific AudioTrack or MediaPlayer instance,
+ * the application must specify the audio session ID of that instance when creating the AudioEffect.
+ * (see {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions).
+ * <p>NOTE: attaching insert effects (equalizer, bass boost, virtualizer) to the global audio output
+ * mix by use of session 0 is deprecated.
+ * <p>Creating an AudioEffect object will create the corresponding effect engine in the audio
+ * framework if no instance of the same effect type exists in the specified audio session.
+ * If one exists, this instance will be used.
+ * <p>The application creating the AudioEffect object (or a derived class) will either receive
+ * control of the effect engine or not depending on the priority parameter. If priority is higher
+ * than the priority used by the current effect engine owner, the control will be transfered to the
+ * new object. Otherwise control will remain with the previous object. In this case, the new
+ * application will be notified of changes in effect engine state or control ownership by the
+ * appropriate listener.
+ */
+
+public class AudioEffect {
+ static {
+ System.loadLibrary("audioeffect_jni");
+ native_init();
+ }
+
+ private final static String TAG = "AudioEffect-JAVA";
+
+ // effect type UUIDs are taken from hardware/libhardware/include/hardware/audio_effect.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
+ */
+ /**
+ * UUID for environmental reverberation effect
+ */
+ public static final UUID EFFECT_TYPE_ENV_REVERB = UUID
+ .fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e");
+ /**
+ * UUID for preset reverberation 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");
+
+ /**
+ * UUIDs for effect types not covered by OpenSL ES.
+ */
+ /**
+ * UUID for Automatic Gain Control (AGC)
+ */
+ public static final UUID EFFECT_TYPE_AGC = UUID
+ .fromString("0a8abfe0-654c-11e0-ba26-0002a5d5c51b");
+
+ /**
+ * UUID for Acoustic Echo Canceler (AEC)
+ */
+ public static final UUID EFFECT_TYPE_AEC = UUID
+ .fromString("7b491460-8d4d-11e0-bd61-0002a5d5c51b");
+
+ /**
+ * UUID for Noise Suppressor (NS)
+ */
+ public static final UUID EFFECT_TYPE_NS = UUID
+ .fromString("58b4b260-8e06-11e0-aa8e-0002a5d5c51b");
+
+ /**
+ * UUID for Loudness Enhancer
+ */
+ public static final UUID EFFECT_TYPE_LOUDNESS_ENHANCER = UUID
+ .fromString("fe3199be-aed0-413f-87bb-11260eb63cf1");
+
+ /**
+ * Null effect UUID. Used when the UUID for effect type of
+ * @hide
+ */
+ public static final UUID EFFECT_TYPE_NULL = UUID
+ .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210");
+
+ /**
+ * State of an AudioEffect object that was not successfully initialized upon
+ * creation
+ * @hide
+ */
+ public static final int STATE_UNINITIALIZED = 0;
+ /**
+ * State of an AudioEffect object that is ready to be used.
+ * @hide
+ */
+ public static final int STATE_INITIALIZED = 1;
+
+ // to keep in sync with
+ // frameworks/base/include/media/AudioEffect.h
+ /**
+ * Event id for engine control ownership change notification.
+ * @hide
+ */
+ public static final int NATIVE_EVENT_CONTROL_STATUS = 0;
+ /**
+ * Event id for engine state change notification.
+ * @hide
+ */
+ public static final int NATIVE_EVENT_ENABLED_STATUS = 1;
+ /**
+ * Event id for engine parameter change notification.
+ * @hide
+ */
+ public static final int NATIVE_EVENT_PARAMETER_CHANGED = 2;
+
+ /**
+ * Successful operation.
+ */
+ public static final int SUCCESS = 0;
+ /**
+ * Unspecified error.
+ */
+ public static final int ERROR = -1;
+ /**
+ * Internal operation status. Not returned by any method.
+ */
+ public static final int ALREADY_EXISTS = -2;
+ /**
+ * Operation failed due to bad object initialization.
+ */
+ public static final int ERROR_NO_INIT = -3;
+ /**
+ * Operation failed due to bad parameter value.
+ */
+ public static final int ERROR_BAD_VALUE = -4;
+ /**
+ * Operation failed because it was requested in wrong state.
+ */
+ public static final int ERROR_INVALID_OPERATION = -5;
+ /**
+ * Operation failed due to lack of memory.
+ */
+ public static final int ERROR_NO_MEMORY = -6;
+ /**
+ * Operation failed due to dead remote object.
+ */
+ public static final int ERROR_DEAD_OBJECT = -7;
+
+ /**
+ * The effect descriptor contains information on a particular effect implemented in the
+ * audio framework:<br>
+ * <ul>
+ * <li>type: UUID identifying the effect type. May be one of:
+ * {@link AudioEffect#EFFECT_TYPE_AEC}, {@link AudioEffect#EFFECT_TYPE_AGC},
+ * {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB},
+ * {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
+ * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}, {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}.
+ * </li>
+ * <li>uuid: UUID for this particular implementation</li>
+ * <li>connectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li>
+ * <li>name: human readable effect name</li>
+ * <li>implementor: human readable effect implementor name</li>
+ * </ul>
+ * The method {@link #queryEffects()} returns an array of Descriptors to facilitate effects
+ * enumeration.
+ */
+ public static class Descriptor {
+
+ public Descriptor() {
+ }
+
+ /**
+ * @param type UUID identifying the effect type. May be one of:
+ * {@link AudioEffect#EFFECT_TYPE_AEC}, {@link AudioEffect#EFFECT_TYPE_AGC},
+ * {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB},
+ * {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
+ * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB},
+ * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}.
+ * @param uuid UUID for this particular implementation
+ * @param connectMode {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}
+ * @param name human readable effect name
+ * @param implementor human readable effect implementor name
+ *
+ */
+ public Descriptor(String type, String uuid, String connectMode,
+ String name, String implementor) {
+ this.type = UUID.fromString(type);
+ this.uuid = UUID.fromString(uuid);
+ this.connectMode = connectMode;
+ this.name = name;
+ this.implementor = implementor;
+ }
+
+ /**
+ * Indicates the generic type of the effect (Equalizer, Bass boost ...).
+ * One of {@link AudioEffect#EFFECT_TYPE_AEC},
+ * {@link AudioEffect#EFFECT_TYPE_AGC}, {@link AudioEffect#EFFECT_TYPE_BASS_BOOST},
+ * {@link AudioEffect#EFFECT_TYPE_ENV_REVERB}, {@link AudioEffect#EFFECT_TYPE_EQUALIZER},
+ * {@link AudioEffect#EFFECT_TYPE_NS}, {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}
+ * or {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}.<br>
+ * For reverberation, bass boost, EQ and virtualizer, the UUID
+ * corresponds to the OpenSL ES Interface ID.
+ */
+ public UUID type;
+ /**
+ * Indicates the particular implementation of the effect in that type. Several effects
+ * can have the same type but this uuid is unique to a given implementation.
+ */
+ public UUID uuid;
+ /**
+ * Indicates if the effect is of insert category {@link #EFFECT_INSERT} or auxiliary
+ * category {@link #EFFECT_AUXILIARY}.
+ * Insert effects (typically an {@link Equalizer}) are applied
+ * to the entire audio source and usually not shared by several sources. Auxiliary effects
+ * (typically a reverberator) are applied to part of the signal (wet) and the effect output
+ * is added to the original signal (dry).
+ * Audio pre processing are applied to audio captured on a particular
+ * {@link android.media.AudioRecord}.
+ */
+ public String connectMode;
+ /**
+ * Human readable effect name
+ */
+ public String name;
+ /**
+ * Human readable effect implementor name
+ */
+ public String implementor;
+ };
+
+ /**
+ * Effect connection mode is insert. Specifying an audio session ID when creating the effect
+ * will insert this effect after all players in the same audio session.
+ */
+ public static final String EFFECT_INSERT = "Insert";
+ /**
+ * Effect connection mode is auxiliary.
+ * <p>Auxiliary effects must be created on session 0 (global output mix). In order for a
+ * MediaPlayer or AudioTrack to be fed into this effect, they must be explicitely attached to
+ * this effect and a send level must be specified.
+ * <p>Use the effect ID returned by {@link #getId()} to designate this particular effect when
+ * attaching it to the MediaPlayer or AudioTrack.
+ */
+ public static final String EFFECT_AUXILIARY = "Auxiliary";
+ /**
+ * Effect connection mode is pre processing.
+ * The audio pre processing effects are attached to an audio input (AudioRecord).
+ * @hide
+ */
+ public static final String EFFECT_PRE_PROCESSING = "Pre Processing";
+
+ // --------------------------------------------------------------------------
+ // Member variables
+ // --------------------
+ /**
+ * Indicates the state of the AudioEffect instance
+ */
+ private int mState = STATE_UNINITIALIZED;
+ /**
+ * Lock to synchronize access to mState
+ */
+ private final Object mStateLock = new Object();
+ /**
+ * System wide unique effect ID
+ */
+ private int mId;
+
+ // accessed by native methods
+ private long mNativeAudioEffect;
+ private long mJniData;
+
+ /**
+ * Effect descriptor
+ */
+ private Descriptor mDescriptor;
+
+ /**
+ * Listener for effect engine state change notifications.
+ *
+ * @see #setEnableStatusListener(OnEnableStatusChangeListener)
+ */
+ private OnEnableStatusChangeListener mEnableStatusChangeListener = null;
+ /**
+ * Listener for effect engine control ownership change notifications.
+ *
+ * @see #setControlStatusListener(OnControlStatusChangeListener)
+ */
+ private OnControlStatusChangeListener mControlChangeStatusListener = null;
+ /**
+ * Listener for effect engine control ownership change notifications.
+ *
+ * @see #setParameterListener(OnParameterChangeListener)
+ */
+ private OnParameterChangeListener mParameterChangeListener = null;
+ /**
+ * Lock to protect listeners updates against event notifications
+ * @hide
+ */
+ public final Object mListenerLock = new Object();
+ /**
+ * Handler for events coming from the native code
+ * @hide
+ */
+ public NativeEventHandler mNativeEventHandler = null;
+
+ // --------------------------------------------------------------------------
+ // Constructor, Finalize
+ // --------------------
+ /**
+ * Class constructor.
+ *
+ * @param type type of effect engine created. See {@link #EFFECT_TYPE_ENV_REVERB},
+ * {@link #EFFECT_TYPE_EQUALIZER} ... Types corresponding to
+ * built-in effects are defined by AudioEffect class. Other types
+ * can be specified provided they correspond an existing OpenSL
+ * ES interface ID and the corresponsing effect is available on
+ * the platform. If an unspecified effect type is requested, the
+ * constructor with throw the IllegalArgumentException. This
+ * parameter can be set to {@link #EFFECT_TYPE_NULL} in which
+ * case only the uuid will be used to select the effect.
+ * @param uuid unique identifier of a particular effect implementation.
+ * Must be specified if the caller wants to use a particular
+ * implementation of an effect type. This parameter can be set to
+ * {@link #EFFECT_TYPE_NULL} in which case only the type will
+ * be used to select the effect.
+ * @param priority the priority level requested by the application for
+ * controlling the effect engine. As the same effect engine can
+ * be shared by several applications, this parameter indicates
+ * how much the requesting application needs control of effect
+ * parameters. The normal priority is 0, above normal is a
+ * positive number, below normal a negative number.
+ * @param audioSession system wide unique audio session identifier.
+ * The effect will be attached to the MediaPlayer or AudioTrack in
+ * the same audio session.
+ *
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ * @hide
+ */
+
+ public AudioEffect(UUID type, UUID uuid, int priority, int audioSession)
+ throws IllegalArgumentException, UnsupportedOperationException,
+ RuntimeException {
+ int[] id = new int[1];
+ Descriptor[] desc = new Descriptor[1];
+ // native initialization
+ int initResult = native_setup(new WeakReference<AudioEffect>(this),
+ type.toString(), uuid.toString(), priority, audioSession, id,
+ desc, ActivityThread.currentOpPackageName());
+ if (initResult != SUCCESS && initResult != ALREADY_EXISTS) {
+ Log.e(TAG, "Error code " + initResult
+ + " when initializing AudioEffect.");
+ switch (initResult) {
+ case ERROR_BAD_VALUE:
+ throw (new IllegalArgumentException("Effect type: " + type
+ + " not supported."));
+ case ERROR_INVALID_OPERATION:
+ throw (new UnsupportedOperationException(
+ "Effect library not loaded"));
+ default:
+ throw (new RuntimeException(
+ "Cannot initialize effect engine for type: " + type
+ + " Error: " + initResult));
+ }
+ }
+ mId = id[0];
+ mDescriptor = desc[0];
+ synchronized (mStateLock) {
+ mState = STATE_INITIALIZED;
+ }
+ }
+
+ /**
+ * Releases the native AudioEffect resources. It is a good practice to
+ * release the effect engine when not in use as control can be returned to
+ * other applications or the native resources released.
+ */
+ public void release() {
+ synchronized (mStateLock) {
+ native_release();
+ mState = STATE_UNINITIALIZED;
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ native_finalize();
+ }
+
+ /**
+ * Get the effect descriptor.
+ *
+ * @see android.media.audiofx.AudioEffect.Descriptor
+ * @throws IllegalStateException
+ */
+ public Descriptor getDescriptor() throws IllegalStateException {
+ checkState("getDescriptor()");
+ return mDescriptor;
+ }
+
+ // --------------------------------------------------------------------------
+ // Effects Enumeration
+ // --------------------
+
+ /**
+ * Query all effects available on the platform. Returns an array of
+ * {@link android.media.audiofx.AudioEffect.Descriptor} objects
+ *
+ * @throws IllegalStateException
+ */
+
+ static public Descriptor[] queryEffects() {
+ return (Descriptor[]) native_query_effects();
+ }
+
+ /**
+ * Query all audio pre-processing effects applied to the AudioRecord with the supplied
+ * audio session ID. Returns an array of {@link android.media.audiofx.AudioEffect.Descriptor}
+ * objects.
+ * @param audioSession system wide unique audio session identifier.
+ * @throws IllegalStateException
+ * @hide
+ */
+
+ static public Descriptor[] queryPreProcessings(int audioSession) {
+ return (Descriptor[]) native_query_pre_processing(audioSession);
+ }
+
+ /**
+ * Checks if the device implements the specified effect type.
+ * @param type the requested effect type.
+ * @return true if the device implements the specified effect type, false otherwise.
+ * @hide
+ */
+ public static boolean isEffectTypeAvailable(UUID type) {
+ AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+ if (desc == null) {
+ return false;
+ }
+
+ for (int i = 0; i < desc.length; i++) {
+ if (desc[i].type.equals(type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // --------------------------------------------------------------------------
+ // Control methods
+ // --------------------
+
+ /**
+ * Enable or disable the effect.
+ * Creating an audio effect does not automatically apply this effect on the audio source. It
+ * creates the resources necessary to process this effect but the audio signal is still bypassed
+ * through the effect engine. Calling this method will make that the effect is actually applied
+ * or not to the audio content being played in the corresponding audio session.
+ *
+ * @param enabled the requested enable state
+ * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION}
+ * or {@link #ERROR_DEAD_OBJECT} in case of failure.
+ * @throws IllegalStateException
+ */
+ public int setEnabled(boolean enabled) throws IllegalStateException {
+ checkState("setEnabled()");
+ return native_setEnabled(enabled);
+ }
+
+ /**
+ * Set effect parameter. The setParameter method is provided in several
+ * forms addressing most common parameter formats. This form is the most
+ * generic one where the parameter and its value are both specified as an
+ * array of bytes. The parameter and value type and length are therefore
+ * totally free. For standard effect defined by OpenSL ES, the parameter
+ * format and values must match the definitions in the corresponding OpenSL
+ * ES interface.
+ *
+ * @param param the identifier of the parameter to set
+ * @param value the new value for the specified parameter
+ * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE},
+ * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or
+ * {@link #ERROR_DEAD_OBJECT} in case of failure
+ * @throws IllegalStateException
+ * @hide
+ */
+ public int setParameter(byte[] param, byte[] value)
+ throws IllegalStateException {
+ checkState("setParameter()");
+ return native_setParameter(param.length, param, value.length, value);
+ }
+
+ /**
+ * Set effect parameter. The parameter and its value are integers.
+ *
+ * @see #setParameter(byte[], byte[])
+ * @hide
+ */
+ public int setParameter(int param, int value) throws IllegalStateException {
+ byte[] p = intToByteArray(param);
+ byte[] v = intToByteArray(value);
+ return setParameter(p, v);
+ }
+
+ /**
+ * Set effect parameter. The parameter is an integer and the value is a
+ * short integer.
+ *
+ * @see #setParameter(byte[], byte[])
+ * @hide
+ */
+ public int setParameter(int param, short value)
+ throws IllegalStateException {
+ byte[] p = intToByteArray(param);
+ byte[] v = shortToByteArray(value);
+ return setParameter(p, v);
+ }
+
+ /**
+ * Set effect parameter. The parameter is an integer and the value is an
+ * array of bytes.
+ *
+ * @see #setParameter(byte[], byte[])
+ * @hide
+ */
+ public int setParameter(int param, byte[] value)
+ throws IllegalStateException {
+ byte[] p = intToByteArray(param);
+ return setParameter(p, value);
+ }
+
+ /**
+ * Set effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is also an array of 1 or 2 integers
+ *
+ * @see #setParameter(byte[], byte[])
+ * @hide
+ */
+ public int setParameter(int[] param, int[] value)
+ throws IllegalStateException {
+ if (param.length > 2 || value.length > 2) {
+ return ERROR_BAD_VALUE;
+ }
+ byte[] p = intToByteArray(param[0]);
+ if (param.length > 1) {
+ byte[] p2 = intToByteArray(param[1]);
+ p = concatArrays(p, p2);
+ }
+ byte[] v = intToByteArray(value[0]);
+ if (value.length > 1) {
+ byte[] v2 = intToByteArray(value[1]);
+ v = concatArrays(v, v2);
+ }
+ return setParameter(p, v);
+ }
+
+ /**
+ * Set effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is an array of 1 or 2 short integers
+ *
+ * @see #setParameter(byte[], byte[])
+ * @hide
+ */
+ public int setParameter(int[] param, short[] value)
+ throws IllegalStateException {
+ if (param.length > 2 || value.length > 2) {
+ return ERROR_BAD_VALUE;
+ }
+ byte[] p = intToByteArray(param[0]);
+ if (param.length > 1) {
+ byte[] p2 = intToByteArray(param[1]);
+ p = concatArrays(p, p2);
+ }
+
+ byte[] v = shortToByteArray(value[0]);
+ if (value.length > 1) {
+ byte[] v2 = shortToByteArray(value[1]);
+ v = concatArrays(v, v2);
+ }
+ return setParameter(p, v);
+ }
+
+ /**
+ * Set effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is an array of bytes
+ *
+ * @see #setParameter(byte[], byte[])
+ * @hide
+ */
+ public int setParameter(int[] param, byte[] value)
+ throws IllegalStateException {
+ if (param.length > 2) {
+ return ERROR_BAD_VALUE;
+ }
+ byte[] p = intToByteArray(param[0]);
+ if (param.length > 1) {
+ byte[] p2 = intToByteArray(param[1]);
+ p = concatArrays(p, p2);
+ }
+ return setParameter(p, value);
+ }
+
+ /**
+ * Get effect parameter. The getParameter method is provided in several
+ * forms addressing most common parameter formats. This form is the most
+ * generic one where the parameter and its value are both specified as an
+ * array of bytes. The parameter and value type and length are therefore
+ * totally free.
+ *
+ * @param param the identifier of the parameter to set
+ * @param value the new value for the specified parameter
+ * @return the number of meaningful bytes in value array in case of success or
+ * {@link #ERROR_BAD_VALUE}, {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION}
+ * or {@link #ERROR_DEAD_OBJECT} in case of failure.
+ * @throws IllegalStateException
+ * @hide
+ */
+ public int getParameter(byte[] param, byte[] value)
+ throws IllegalStateException {
+ checkState("getParameter()");
+ return native_getParameter(param.length, param, value.length, value);
+ }
+
+ /**
+ * Get effect parameter. The parameter is an integer and the value is an
+ * array of bytes.
+ *
+ * @see #getParameter(byte[], byte[])
+ * @hide
+ */
+ public int getParameter(int param, byte[] value)
+ throws IllegalStateException {
+ byte[] p = intToByteArray(param);
+
+ return getParameter(p, value);
+ }
+
+ /**
+ * Get effect parameter. The parameter is an integer and the value is an
+ * array of 1 or 2 integers
+ *
+ * @see #getParameter(byte[], byte[])
+ * In case of success, returns the number of meaningful integers in value array.
+ * @hide
+ */
+ public int getParameter(int param, int[] value)
+ throws IllegalStateException {
+ if (value.length > 2) {
+ return ERROR_BAD_VALUE;
+ }
+ byte[] p = intToByteArray(param);
+
+ byte[] v = new byte[value.length * 4];
+
+ int status = getParameter(p, v);
+
+ if (status == 4 || status == 8) {
+ value[0] = byteArrayToInt(v);
+ if (status == 8) {
+ value[1] = byteArrayToInt(v, 4);
+ }
+ status /= 4;
+ } else {
+ status = ERROR;
+ }
+ return status;
+ }
+
+ /**
+ * Get effect parameter. The parameter is an integer and the value is an
+ * array of 1 or 2 short integers
+ *
+ * @see #getParameter(byte[], byte[])
+ * In case of success, returns the number of meaningful short integers in value array.
+ * @hide
+ */
+ public int getParameter(int param, short[] value)
+ throws IllegalStateException {
+ if (value.length > 2) {
+ return ERROR_BAD_VALUE;
+ }
+ byte[] p = intToByteArray(param);
+
+ byte[] v = new byte[value.length * 2];
+
+ int status = getParameter(p, v);
+
+ if (status == 2 || status == 4) {
+ value[0] = byteArrayToShort(v);
+ if (status == 4) {
+ value[1] = byteArrayToShort(v, 2);
+ }
+ status /= 2;
+ } else {
+ status = ERROR;
+ }
+ return status;
+ }
+
+ /**
+ * Get effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is also an array of 1 or 2 integers
+ *
+ * @see #getParameter(byte[], byte[])
+ * In case of success, the returns the number of meaningful integers in value array.
+ * @hide
+ */
+ public int getParameter(int[] param, int[] value)
+ throws IllegalStateException {
+ if (param.length > 2 || value.length > 2) {
+ return ERROR_BAD_VALUE;
+ }
+ byte[] p = intToByteArray(param[0]);
+ if (param.length > 1) {
+ byte[] p2 = intToByteArray(param[1]);
+ p = concatArrays(p, p2);
+ }
+ byte[] v = new byte[value.length * 4];
+
+ int status = getParameter(p, v);
+
+ if (status == 4 || status == 8) {
+ value[0] = byteArrayToInt(v);
+ if (status == 8) {
+ value[1] = byteArrayToInt(v, 4);
+ }
+ status /= 4;
+ } else {
+ status = ERROR;
+ }
+ return status;
+ }
+
+ /**
+ * Get effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is an array of 1 or 2 short integers
+ *
+ * @see #getParameter(byte[], byte[])
+ * In case of success, returns the number of meaningful short integers in value array.
+ * @hide
+ */
+ public int getParameter(int[] param, short[] value)
+ throws IllegalStateException {
+ if (param.length > 2 || value.length > 2) {
+ return ERROR_BAD_VALUE;
+ }
+ byte[] p = intToByteArray(param[0]);
+ if (param.length > 1) {
+ byte[] p2 = intToByteArray(param[1]);
+ p = concatArrays(p, p2);
+ }
+ byte[] v = new byte[value.length * 2];
+
+ int status = getParameter(p, v);
+
+ if (status == 2 || status == 4) {
+ value[0] = byteArrayToShort(v);
+ if (status == 4) {
+ value[1] = byteArrayToShort(v, 2);
+ }
+ status /= 2;
+ } else {
+ status = ERROR;
+ }
+ return status;
+ }
+
+ /**
+ * Get effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is an array of bytes
+ *
+ * @see #getParameter(byte[], byte[])
+ * @hide
+ */
+ public int getParameter(int[] param, byte[] value)
+ throws IllegalStateException {
+ if (param.length > 2) {
+ return ERROR_BAD_VALUE;
+ }
+ byte[] p = intToByteArray(param[0]);
+ if (param.length > 1) {
+ byte[] p2 = intToByteArray(param[1]);
+ p = concatArrays(p, p2);
+ }
+
+ return getParameter(p, value);
+ }
+
+ /**
+ * Send a command to the effect engine. This method is intended to send
+ * proprietary commands to a particular effect implementation.
+ * In case of success, returns the number of meaningful bytes in reply array.
+ * In case of failure, the returned value is negative and implementation specific.
+ * @hide
+ */
+ public int command(int cmdCode, byte[] command, byte[] reply)
+ throws IllegalStateException {
+ checkState("command()");
+ return native_command(cmdCode, command.length, command, reply.length, reply);
+ }
+
+ // --------------------------------------------------------------------------
+ // Getters
+ // --------------------
+
+ /**
+ * Returns effect unique identifier. This system wide unique identifier can
+ * be used to attach this effect to a MediaPlayer or an AudioTrack when the
+ * effect is an auxiliary effect (Reverb)
+ *
+ * @return the effect identifier.
+ * @throws IllegalStateException
+ */
+ public int getId() throws IllegalStateException {
+ checkState("getId()");
+ return mId;
+ }
+
+ /**
+ * Returns effect enabled state
+ *
+ * @return true if the effect is enabled, false otherwise.
+ * @throws IllegalStateException
+ */
+ public boolean getEnabled() throws IllegalStateException {
+ checkState("getEnabled()");
+ return native_getEnabled();
+ }
+
+ /**
+ * Checks if this AudioEffect object is controlling the effect engine.
+ *
+ * @return true if this instance has control of effect engine, false
+ * otherwise.
+ * @throws IllegalStateException
+ */
+ public boolean hasControl() throws IllegalStateException {
+ checkState("hasControl()");
+ return native_hasControl();
+ }
+
+ // --------------------------------------------------------------------------
+ // Initialization / configuration
+ // --------------------
+ /**
+ * Sets the listener AudioEffect notifies when the effect engine is enabled
+ * or disabled.
+ *
+ * @param listener
+ */
+ public void setEnableStatusListener(OnEnableStatusChangeListener listener) {
+ synchronized (mListenerLock) {
+ mEnableStatusChangeListener = listener;
+ }
+ if ((listener != null) && (mNativeEventHandler == null)) {
+ createNativeEventHandler();
+ }
+ }
+
+ /**
+ * Sets the listener AudioEffect notifies when the effect engine control is
+ * taken or returned.
+ *
+ * @param listener
+ */
+ public void setControlStatusListener(OnControlStatusChangeListener listener) {
+ synchronized (mListenerLock) {
+ mControlChangeStatusListener = listener;
+ }
+ if ((listener != null) && (mNativeEventHandler == null)) {
+ createNativeEventHandler();
+ }
+ }
+
+ /**
+ * Sets the listener AudioEffect notifies when a parameter is changed.
+ *
+ * @param listener
+ * @hide
+ */
+ public void setParameterListener(OnParameterChangeListener listener) {
+ synchronized (mListenerLock) {
+ mParameterChangeListener = listener;
+ }
+ if ((listener != null) && (mNativeEventHandler == null)) {
+ createNativeEventHandler();
+ }
+ }
+
+ // Convenience method for the creation of the native event handler
+ // It is called only when a non-null event listener is set.
+ // precondition:
+ // mNativeEventHandler is null
+ private void createNativeEventHandler() {
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mNativeEventHandler = new NativeEventHandler(this, looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ mNativeEventHandler = new NativeEventHandler(this, looper);
+ } else {
+ mNativeEventHandler = null;
+ }
+ }
+
+ // ---------------------------------------------------------
+ // Interface definitions
+ // --------------------
+ /**
+ * The OnEnableStatusChangeListener interface defines a method called by the AudioEffect
+ * when a the enabled state of the effect engine was changed by the controlling application.
+ */
+ public interface OnEnableStatusChangeListener {
+ /**
+ * Called on the listener to notify it that the effect engine has been
+ * enabled or disabled.
+ * @param effect the effect on which the interface is registered.
+ * @param enabled new effect state.
+ */
+ void onEnableStatusChange(AudioEffect effect, boolean enabled);
+ }
+
+ /**
+ * The OnControlStatusChangeListener interface defines a method called by the AudioEffect
+ * when a the control of the effect engine is gained or lost by the application
+ */
+ public interface OnControlStatusChangeListener {
+ /**
+ * Called on the listener to notify it that the effect engine control
+ * has been taken or returned.
+ * @param effect the effect on which the interface is registered.
+ * @param controlGranted true if the application has been granted control of the effect
+ * engine, false otherwise.
+ */
+ void onControlStatusChange(AudioEffect effect, boolean controlGranted);
+ }
+
+ /**
+ * The OnParameterChangeListener interface defines a method called by the AudioEffect
+ * when a parameter is changed in the effect engine by the controlling application.
+ * @hide
+ */
+ public interface OnParameterChangeListener {
+ /**
+ * Called on the listener to notify it that a parameter value has changed.
+ * @param effect the effect on which the interface is registered.
+ * @param status status of the set parameter operation.
+ * @param param ID of the modified parameter.
+ * @param value the new parameter value.
+ */
+ void onParameterChange(AudioEffect effect, int status, byte[] param,
+ byte[] value);
+ }
+
+
+ // -------------------------------------------------------------------------
+ // Audio Effect Control panel intents
+ // -------------------------------------------------------------------------
+
+ /**
+ * Intent to launch an audio effect control panel UI.
+ * <p>The goal of this intent is to enable separate implementations of music/media player
+ * applications and audio effect control application or services.
+ * This will allow platform vendors to offer more advanced control options for standard effects
+ * or control for platform specific effects.
+ * <p>The intent carries a number of extras used by the player application to communicate
+ * necessary pieces of information to the control panel application.
+ * <p>The calling application must use the
+ * {@link android.app.Activity#startActivityForResult(Intent, int)} method to launch the
+ * control panel so that its package name is indicated and used by the control panel
+ * application to keep track of changes for this particular application.
+ * <p>The {@link #EXTRA_AUDIO_SESSION} extra will indicate an audio session to which the
+ * audio effects should be applied. If no audio session is specified, either one of the
+ * follownig will happen:
+ * <p>- If an audio session was previously opened by the calling application with
+ * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intent, the effect changes will
+ * be applied to that session.
+ * <p>- If no audio session is opened, the changes will be stored in the package specific
+ * storage area and applied whenever a new audio session is opened by this application.
+ * <p>The {@link #EXTRA_CONTENT_TYPE} extra will help the control panel application
+ * customize both the UI layout and the default audio effect settings if none are already
+ * stored for the calling application.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL =
+ "android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL";
+
+ /**
+ * Intent to signal to the effect control application or service that a new audio session
+ * is opened and requires audio effects to be applied.
+ * <p>This is different from {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} in that no
+ * UI should be displayed in this case. Music player applications can broadcast this intent
+ * before starting playback to make sure that any audio effect settings previously selected
+ * by the user are applied.
+ * <p>The effect control application receiving this intent will look for previously stored
+ * settings for the calling application, create all required audio effects and apply the
+ * effect settings to the specified audio session.
+ * <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the
+ * audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory.
+ * <p>If no stored settings are found for the calling application, default settings for the
+ * content type indicated by {@link #EXTRA_CONTENT_TYPE} will be applied. The default settings
+ * for a given content type are platform specific.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION =
+ "android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION";
+
+ /**
+ * Intent to signal to the effect control application or service that an audio session
+ * is closed and that effects should not be applied anymore.
+ * <p>The effect control application receiving this intent will delete all effects on
+ * this session and store current settings in package specific storage.
+ * <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the
+ * audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory.
+ * <p>It is good practice for applications to broadcast this intent when music playback stops
+ * and/or when exiting to free system resources consumed by audio effect engines.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION =
+ "android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION";
+
+ /**
+ * Contains the ID of the audio session the effects should be applied to.
+ * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL},
+ * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
+ * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
+ * <p>The extra value is of type int and is the audio session ID.
+ * @see android.media.MediaPlayer#getAudioSessionId() for details on audio sessions.
+ */
+ public static final String EXTRA_AUDIO_SESSION = "android.media.extra.AUDIO_SESSION";
+
+ /**
+ * Contains the package name of the calling application.
+ * <p>This extra is for use with {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
+ * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
+ * <p>The extra value is a string containing the full package name.
+ */
+ public static final String EXTRA_PACKAGE_NAME = "android.media.extra.PACKAGE_NAME";
+
+ /**
+ * Indicates which type of content is played by the application.
+ * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} and
+ * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intents.
+ * <p>This information is used by the effect control application to customize UI and select
+ * appropriate default effect settings. The content type is one of the following:
+ * <ul>
+ * <li>{@link #CONTENT_TYPE_MUSIC}</li>
+ * <li>{@link #CONTENT_TYPE_MOVIE}</li>
+ * <li>{@link #CONTENT_TYPE_GAME}</li>
+ * <li>{@link #CONTENT_TYPE_VOICE}</li>
+ * </ul>
+ * If omitted, the content type defaults to {@link #CONTENT_TYPE_MUSIC}.
+ */
+ public static final String EXTRA_CONTENT_TYPE = "android.media.extra.CONTENT_TYPE";
+
+ /**
+ * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is music
+ */
+ public static final int CONTENT_TYPE_MUSIC = 0;
+ /**
+ * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is video or movie
+ */
+ public static final int CONTENT_TYPE_MOVIE = 1;
+ /**
+ * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is game audio
+ */
+ public static final int CONTENT_TYPE_GAME = 2;
+ /**
+ * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is voice audio
+ */
+ public static final int CONTENT_TYPE_VOICE = 3;
+
+
+ // ---------------------------------------------------------
+ // Inner classes
+ // --------------------
+ /**
+ * Helper class to handle the forwarding of native events to the appropriate
+ * listeners
+ */
+ private class NativeEventHandler extends Handler {
+ private AudioEffect mAudioEffect;
+
+ public NativeEventHandler(AudioEffect ae, Looper looper) {
+ super(looper);
+ mAudioEffect = ae;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (mAudioEffect == null) {
+ return;
+ }
+ switch (msg.what) {
+ case NATIVE_EVENT_ENABLED_STATUS:
+ OnEnableStatusChangeListener enableStatusChangeListener = null;
+ synchronized (mListenerLock) {
+ enableStatusChangeListener = mAudioEffect.mEnableStatusChangeListener;
+ }
+ if (enableStatusChangeListener != null) {
+ enableStatusChangeListener.onEnableStatusChange(
+ mAudioEffect, (boolean) (msg.arg1 != 0));
+ }
+ break;
+ case NATIVE_EVENT_CONTROL_STATUS:
+ OnControlStatusChangeListener controlStatusChangeListener = null;
+ synchronized (mListenerLock) {
+ controlStatusChangeListener = mAudioEffect.mControlChangeStatusListener;
+ }
+ if (controlStatusChangeListener != null) {
+ controlStatusChangeListener.onControlStatusChange(
+ mAudioEffect, (boolean) (msg.arg1 != 0));
+ }
+ break;
+ case NATIVE_EVENT_PARAMETER_CHANGED:
+ OnParameterChangeListener parameterChangeListener = null;
+ synchronized (mListenerLock) {
+ parameterChangeListener = mAudioEffect.mParameterChangeListener;
+ }
+ if (parameterChangeListener != null) {
+ // arg1 contains offset of parameter value from start of
+ // byte array
+ int vOffset = msg.arg1;
+ byte[] p = (byte[]) msg.obj;
+ // See effect_param_t in EffectApi.h for psize and vsize
+ // fields offsets
+ int status = byteArrayToInt(p, 0);
+ int psize = byteArrayToInt(p, 4);
+ int vsize = byteArrayToInt(p, 8);
+ byte[] param = new byte[psize];
+ byte[] value = new byte[vsize];
+ System.arraycopy(p, 12, param, 0, psize);
+ System.arraycopy(p, vOffset, value, 0, vsize);
+
+ parameterChangeListener.onParameterChange(mAudioEffect,
+ status, param, value);
+ }
+ break;
+
+ default:
+ Log.e(TAG, "handleMessage() Unknown event type: " + msg.what);
+ break;
+ }
+ }
+ }
+
+ // ---------------------------------------------------------
+ // Java methods called from the native side
+ // --------------------
+ @SuppressWarnings("unused")
+ private static void postEventFromNative(Object effect_ref, int what,
+ int arg1, int arg2, Object obj) {
+ AudioEffect effect = (AudioEffect) ((WeakReference) effect_ref).get();
+ if (effect == null) {
+ return;
+ }
+ if (effect.mNativeEventHandler != null) {
+ Message m = effect.mNativeEventHandler.obtainMessage(what, arg1,
+ arg2, obj);
+ effect.mNativeEventHandler.sendMessage(m);
+ }
+
+ }
+
+ // ---------------------------------------------------------
+ // Native methods called from the Java side
+ // --------------------
+
+ private static native final void native_init();
+
+ private native final int native_setup(Object audioeffect_this, String type,
+ String uuid, int priority, int audioSession, int[] id, Object[] desc,
+ String opPackageName);
+
+ private native final void native_finalize();
+
+ private native final void native_release();
+
+ private native final int native_setEnabled(boolean enabled);
+
+ private native final boolean native_getEnabled();
+
+ private native final boolean native_hasControl();
+
+ private native final int native_setParameter(int psize, byte[] param,
+ int vsize, byte[] value);
+
+ private native final int native_getParameter(int psize, byte[] param,
+ int vsize, byte[] value);
+
+ private native final int native_command(int cmdCode, int cmdSize,
+ byte[] cmdData, int repSize, byte[] repData);
+
+ private static native Object[] native_query_effects();
+
+ private static native Object[] native_query_pre_processing(int audioSession);
+
+ // ---------------------------------------------------------
+ // Utility methods
+ // ------------------
+
+ /**
+ * @hide
+ */
+ public void checkState(String methodName) throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState != STATE_INITIALIZED) {
+ throw (new IllegalStateException(methodName
+ + " called on uninitialized AudioEffect."));
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void checkStatus(int status) {
+ if (isError(status)) {
+ switch (status) {
+ case AudioEffect.ERROR_BAD_VALUE:
+ throw (new IllegalArgumentException(
+ "AudioEffect: bad parameter value"));
+ case AudioEffect.ERROR_INVALID_OPERATION:
+ throw (new UnsupportedOperationException(
+ "AudioEffect: invalid parameter operation"));
+ default:
+ throw (new RuntimeException("AudioEffect: set/get parameter error"));
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean isError(int status) {
+ return (status < 0);
+ }
+
+ /**
+ * @hide
+ */
+ public static int byteArrayToInt(byte[] valueBuf) {
+ return byteArrayToInt(valueBuf, 0);
+
+ }
+
+ /**
+ * @hide
+ */
+ public static int byteArrayToInt(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getInt(offset);
+
+ }
+
+ /**
+ * @hide
+ */
+ public static byte[] intToByteArray(int value) {
+ ByteBuffer converter = ByteBuffer.allocate(4);
+ converter.order(ByteOrder.nativeOrder());
+ converter.putInt(value);
+ return converter.array();
+ }
+
+ /**
+ * @hide
+ */
+ public static short byteArrayToShort(byte[] valueBuf) {
+ return byteArrayToShort(valueBuf, 0);
+ }
+
+ /**
+ * @hide
+ */
+ public static short byteArrayToShort(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getShort(offset);
+
+ }
+
+ /**
+ * @hide
+ */
+ public static byte[] shortToByteArray(short value) {
+ ByteBuffer converter = ByteBuffer.allocate(2);
+ converter.order(ByteOrder.nativeOrder());
+ short sValue = (short) value;
+ converter.putShort(sValue);
+ return converter.array();
+ }
+
+ /**
+ * @hide
+ */
+ public static byte[] concatArrays(byte[]... arrays) {
+ int len = 0;
+ for (byte[] a : arrays) {
+ len += a.length;
+ }
+ byte[] b = new byte[len];
+
+ int offs = 0;
+ for (byte[] a : arrays) {
+ System.arraycopy(a, 0, b, offs, a.length);
+ offs += a.length;
+ }
+ return b;
+ }
+}
diff --git a/android/media/audiofx/AutomaticGainControl.java b/android/media/audiofx/AutomaticGainControl.java
new file mode 100644
index 00000000..a76b4de7
--- /dev/null
+++ b/android/media/audiofx/AutomaticGainControl.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.util.Log;
+
+/**
+ * Automatic Gain Control (AGC).
+ * <p>Automatic Gain Control (AGC) is an audio pre-processor which automatically normalizes the
+ * output of the captured signal by boosting or lowering input from the microphone to match a preset
+ * level so that the output signal level is virtually constant.
+ * AGC can be used by applications where the input signal dynamic range is not important but where
+ * a constant strong capture level is desired.
+ * <p>An application creates a AutomaticGainControl object to instantiate and control an AGC
+ * engine in the audio framework.
+ * <p>To attach the AutomaticGainControl to a particular {@link android.media.AudioRecord},
+ * specify the audio session ID of this AudioRecord when creating the AutomaticGainControl.
+ * The audio session is retrieved by calling
+ * {@link android.media.AudioRecord#getAudioSessionId()} on the AudioRecord instance.
+ * <p>On some devices, an AGC can be inserted by default in the capture path by the platform
+ * according to the {@link android.media.MediaRecorder.AudioSource} used. The application should
+ * call AutomaticGainControl.getEnable() after creating the AGC to check the default AGC activation
+ * state on a particular AudioRecord session.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on
+ * controlling audio effects.
+ */
+
+public class AutomaticGainControl extends AudioEffect {
+
+ private final static String TAG = "AutomaticGainControl";
+
+ /**
+ * Checks if the device implements automatic gain control.
+ * @return true if the device implements automatic gain control, false otherwise.
+ */
+ public static boolean isAvailable() {
+ return AudioEffect.isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_AGC);
+ }
+
+ /**
+ * Creates an AutomaticGainControl and attaches it to the AudioRecord on the audio
+ * session specified.
+ * @param audioSession system wide unique audio session identifier. The AutomaticGainControl
+ * will be applied to the AudioRecord with the same audio session.
+ * @return AutomaticGainControl created or null if the device does not implement AGC.
+ */
+ public static AutomaticGainControl create(int audioSession) {
+ AutomaticGainControl agc = null;
+ try {
+ agc = new AutomaticGainControl(audioSession);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "not implemented on this device "+agc);
+ } catch (UnsupportedOperationException e) {
+ Log.w(TAG, "not enough resources");
+ } catch (RuntimeException e) {
+ Log.w(TAG, "not enough memory");
+ }
+ return agc;
+ }
+
+ /**
+ * Class constructor.
+ * <p> The constructor is not guarantied to succeed and throws the following exceptions:
+ * <ul>
+ * <li>IllegalArgumentException is thrown if the device does not implement an AGC</li>
+ * <li>UnsupportedOperationException is thrown is the resources allocated to audio
+ * pre-procesing are currently exceeded.</li>
+ * <li>RuntimeException is thrown if a memory allocation error occurs.</li>
+ * </ul>
+ *
+ * @param audioSession system wide unique audio session identifier. The AutomaticGainControl
+ * will be applied to the AudioRecord with the same audio session.
+ *
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ private AutomaticGainControl(int audioSession)
+ throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_AGC, EFFECT_TYPE_NULL, 0, audioSession);
+ }
+}
diff --git a/android/media/audiofx/BassBoost.java b/android/media/audiofx/BassBoost.java
new file mode 100644
index 00000000..a46cc223
--- /dev/null
+++ b/android/media/audiofx/BassBoost.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.media.audiofx.AudioEffect;
+import android.util.Log;
+
+import java.util.StringTokenizer;
+
+
+/**
+ * Bass boost is an audio effect to boost or amplify low frequencies of the sound. It is comparable
+ * to a simple equalizer but limited to one band amplification in the low frequency range.
+ * <p>An application creates a BassBoost object to instantiate and control a bass boost engine in
+ * the audio framework.
+ * <p>The methods, parameter types and units exposed by the BassBoost implementation are directly
+ * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/)
+ * for the SLBassBoostItf interface. Please refer to this specification for more details.
+ * <p>To attach the BassBoost to a particular AudioTrack or MediaPlayer, specify the audio session
+ * ID of this AudioTrack or MediaPlayer when constructing the BassBoost.
+ * <p>NOTE: attaching a BassBoost to the global audio output mix by use of session 0 is deprecated.
+ * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on
+ * controlling audio effects.
+ */
+
+public class BassBoost extends AudioEffect {
+
+ private final static String TAG = "BassBoost";
+
+ // These constants must be synchronized with those in
+ // frameworks/base/include/media/EffectBassBoostApi.h
+ /**
+ * Is strength parameter supported by bass boost engine. Parameter ID for getParameter().
+ */
+ public static final int PARAM_STRENGTH_SUPPORTED = 0;
+ /**
+ * Bass boost effect strength. Parameter ID for
+ * {@link android.media.audiofx.BassBoost.OnParameterChangeListener}
+ */
+ public static final int PARAM_STRENGTH = 1;
+
+ /**
+ * Indicates if strength parameter is supported by the bass boost engine
+ */
+ private boolean mStrengthSupported = false;
+
+ /**
+ * Registered listener for parameter changes.
+ */
+ private OnParameterChangeListener mParamListener = null;
+
+ /**
+ * Listener used internally to to receive raw parameter change event from AudioEffect super class
+ */
+ private BaseParameterListener mBaseParamListener = null;
+
+ /**
+ * Lock for access to mParamListener
+ */
+ private final Object mParamListenerLock = new Object();
+
+ /**
+ * Class constructor.
+ * @param priority the priority level requested by the application for controlling the BassBoost
+ * engine. As the same 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. The BassBoost will be
+ * attached to the MediaPlayer or AudioTrack in the same audio session.
+ *
+ * @throws java.lang.IllegalStateException
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ public BassBoost(int priority, int audioSession)
+ throws IllegalStateException, IllegalArgumentException,
+ UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_BASS_BOOST, EFFECT_TYPE_NULL, priority, audioSession);
+
+ if (audioSession == 0) {
+ Log.w(TAG, "WARNING: attaching a BassBoost to global output mix is deprecated!");
+ }
+
+ int[] value = new int[1];
+ checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
+ mStrengthSupported = (value[0] != 0);
+ }
+
+ /**
+ * Indicates whether setting strength is supported. If this method returns false, only one
+ * strength is supported and the setStrength() method always rounds to that value.
+ * @return true is strength parameter is supported, false otherwise
+ */
+ public boolean getStrengthSupported() {
+ return mStrengthSupported;
+ }
+
+ /**
+ * Sets the strength of the bass boost effect. If the implementation does not support per mille
+ * accuracy for setting the strength, it is allowed to round the given strength to the nearest
+ * supported value. You can use the {@link #getRoundedStrength()} method to query the
+ * (possibly rounded) value that was actually set.
+ * @param strength strength of the effect. The valid range for strength strength is [0, 1000],
+ * where 0 per mille designates the mildest effect and 1000 per mille designates the strongest.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setStrength(short strength)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_STRENGTH, strength));
+ }
+
+ /**
+ * Gets the current strength of the effect.
+ * @return the strength of the effect. The valid range for strength is [0, 1000], where 0 per
+ * mille designates the mildest effect and 1000 per mille the strongest
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getRoundedStrength()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ short[] value = new short[1];
+ checkStatus(getParameter(PARAM_STRENGTH, value));
+ return value[0];
+ }
+
+ /**
+ * The OnParameterChangeListener interface defines a method called by the BassBoost when a
+ * parameter value has changed.
+ */
+ public interface OnParameterChangeListener {
+ /**
+ * Method called when a parameter value has changed. The method is called only if the
+ * parameter was changed by another application having the control of the same
+ * BassBoost engine.
+ * @param effect the BassBoost on which the interface is registered.
+ * @param status status of the set parameter operation.
+ * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ...
+ * @param value the new parameter value.
+ */
+ void onParameterChange(BassBoost effect, int status, int param, short value);
+ }
+
+ /**
+ * Listener used internally to receive unformatted parameter change events from AudioEffect
+ * super class.
+ */
+ private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
+ private BaseParameterListener() {
+
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ OnParameterChangeListener l = null;
+
+ synchronized (mParamListenerLock) {
+ if (mParamListener != null) {
+ l = mParamListener;
+ }
+ }
+ if (l != null) {
+ int p = -1;
+ short v = -1;
+
+ if (param.length == 4) {
+ p = byteArrayToInt(param, 0);
+ }
+ if (value.length == 2) {
+ v = byteArrayToShort(value, 0);
+ }
+ if (p != -1 && v != -1) {
+ l.onParameterChange(BassBoost.this, status, p, v);
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers an OnParameterChangeListener interface.
+ * @param listener OnParameterChangeListener interface registered
+ */
+ public void setParameterListener(OnParameterChangeListener listener) {
+ synchronized (mParamListenerLock) {
+ if (mParamListener == null) {
+ mParamListener = listener;
+ mBaseParamListener = new BaseParameterListener();
+ super.setParameterListener(mBaseParamListener);
+ }
+ }
+ }
+
+ /**
+ * The Settings class regroups all bass boost parameters. It is used in
+ * conjuntion with getProperties() and setProperties() methods to backup and restore
+ * all parameters in a single call.
+ */
+ public static class Settings {
+ public short strength;
+
+ public Settings() {
+ }
+
+ /**
+ * Settings class constructor from a key=value; pairs formatted string. The string is
+ * typically returned by Settings.toString() method.
+ * @throws IllegalArgumentException if the string is not correctly formatted.
+ */
+ public Settings(String settings) {
+ StringTokenizer st = new StringTokenizer(settings, "=;");
+ int tokens = st.countTokens();
+ if (st.countTokens() != 3) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ String key = st.nextToken();
+ if (!key.equals("BassBoost")) {
+ throw new IllegalArgumentException(
+ "invalid settings for BassBoost: " + key);
+ }
+ try {
+ key = st.nextToken();
+ if (!key.equals("strength")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ strength = Short.parseShort(st.nextToken());
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("invalid value for key: " + key);
+ }
+ }
+
+ @Override
+ public String toString() {
+ String str = new String (
+ "BassBoost"+
+ ";strength="+Short.toString(strength)
+ );
+ return str;
+ }
+ };
+
+
+ /**
+ * Gets the bass boost properties. This method is useful when a snapshot of current
+ * bass boost settings must be saved by the application.
+ * @return a BassBoost.Settings object containing all current parameters values
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public BassBoost.Settings getProperties()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ Settings settings = new Settings();
+ short[] value = new short[1];
+ checkStatus(getParameter(PARAM_STRENGTH, value));
+ settings.strength = value[0];
+ return settings;
+ }
+
+ /**
+ * Sets the bass boost properties. This method is useful when bass boost settings have to
+ * be applied from a previous backup.
+ * @param settings a BassBoost.Settings object containing the properties to apply
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setProperties(BassBoost.Settings settings)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_STRENGTH, settings.strength));
+ }
+}
diff --git a/android/media/audiofx/EnvironmentalReverb.java b/android/media/audiofx/EnvironmentalReverb.java
new file mode 100644
index 00000000..ef1c4c3e
--- /dev/null
+++ b/android/media/audiofx/EnvironmentalReverb.java
@@ -0,0 +1,661 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.media.audiofx.AudioEffect;
+import java.util.StringTokenizer;
+
+/**
+ * A sound generated within a room travels in many directions. The listener first hears the direct
+ * sound from the source itself. Later, he or she hears discrete echoes caused by sound bouncing off
+ * nearby walls, the ceiling and the floor. As sound waves arrive after undergoing more and more
+ * reflections, individual reflections become indistinguishable and the listener hears continuous
+ * reverberation that decays over time.
+ * Reverb is vital for modeling a listener's environment. It can be used in music applications
+ * to simulate music being played back in various environments, or in games to immerse the
+ * listener within the game's environment.
+ * The EnvironmentalReverb class allows an application to control each reverb engine property in a
+ * global reverb environment and is more suitable for games. For basic control, more suitable for
+ * music applications, it is recommended to use the
+ * {@link android.media.audiofx.PresetReverb} class.
+ * <p>An application creates a EnvironmentalReverb object to instantiate and control a reverb engine
+ * in the audio framework.
+ * <p>The methods, parameter types and units exposed by the EnvironmentalReverb implementation are
+ * directly mapping those defined by the OpenSL ES 1.0.1 Specification
+ * (http://www.khronos.org/opensles/) for the SLEnvironmentalReverbItf interface.
+ * Please refer to this specification for more details.
+ * <p>The EnvironmentalReverb is an output mix auxiliary effect and should be created on
+ * Audio session 0. In order for a MediaPlayer or AudioTrack to be fed into this effect,
+ * they must be explicitely attached to it and a send level must be specified. Use the effect ID
+ * returned by getId() method to designate this particular effect when attaching it to the
+ * MediaPlayer or AudioTrack.
+ * <p>Creating a reverb on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
+ * audio effects.
+ */
+
+public class EnvironmentalReverb extends AudioEffect {
+
+ private final static String TAG = "EnvironmentalReverb";
+
+ // These constants must be synchronized with those in
+ // frameworks/base/include/media/EffectEnvironmentalReverbApi.h
+
+ /**
+ * Room level. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_ROOM_LEVEL = 0;
+ /**
+ * Room HF level. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_ROOM_HF_LEVEL = 1;
+ /**
+ * Decay time. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_DECAY_TIME = 2;
+ /**
+ * Decay HF ratio. Parameter ID for
+ * {@link android.media.audiofx.EnvironmentalReverb.OnParameterChangeListener}
+ */
+ public static final int PARAM_DECAY_HF_RATIO = 3;
+ /**
+ * Early reflections level. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_REFLECTIONS_LEVEL = 4;
+ /**
+ * Early reflections delay. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_REFLECTIONS_DELAY = 5;
+ /**
+ * Reverb level. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_REVERB_LEVEL = 6;
+ /**
+ * Reverb delay. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_REVERB_DELAY = 7;
+ /**
+ * Diffusion. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_DIFFUSION = 8;
+ /**
+ * Density. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_DENSITY = 9;
+
+ // used by setProperties()/getProperties
+ private static final int PARAM_PROPERTIES = 10;
+
+ /**
+ * Registered listener for parameter changes
+ */
+ private OnParameterChangeListener mParamListener = null;
+
+ /**
+ * Listener used internally to to receive raw parameter change event from AudioEffect super
+ * class
+ */
+ private BaseParameterListener mBaseParamListener = null;
+
+ /**
+ * Lock for access to mParamListener
+ */
+ private final Object mParamListenerLock = new Object();
+
+ /**
+ * Class constructor.
+ * @param priority the priority level requested by the application for controlling the
+ * EnvironmentalReverb engine. As the same 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 EnvironmentalReverb will be attached to the MediaPlayer or AudioTrack in the
+ * same audio session. Otherwise, the EnvironmentalReverb will apply to the output mix.
+ * As the EnvironmentalReverb is an auxiliary effect it is recommended to instantiate it on
+ * audio session 0 and to attach it to the MediaPLayer auxiliary output.
+ *
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ public EnvironmentalReverb(int priority, int audioSession)
+ throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_ENV_REVERB, EFFECT_TYPE_NULL, priority, audioSession);
+ }
+
+ /**
+ * Sets the master volume level of the environmental reverb effect.
+ * @param room room level in millibels. The valid range is [-9000, 0].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setRoomLevel(short room)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(room);
+ checkStatus(setParameter(PARAM_ROOM_LEVEL, param));
+ }
+
+ /**
+ * Gets the master volume level of the environmental reverb effect.
+ * @return the room level in millibels.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getRoomLevel()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_ROOM_LEVEL, param));
+ return byteArrayToShort(param);
+ }
+
+ /**
+ * Sets the volume level at 5 kHz relative to the volume level at low frequencies of the
+ * overall reverb effect.
+ * <p>This controls a low-pass filter that will reduce the level of the high-frequency.
+ * @param roomHF high frequency attenuation level in millibels. The valid range is [-9000, 0].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setRoomHFLevel(short roomHF)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(roomHF);
+ checkStatus(setParameter(PARAM_ROOM_HF_LEVEL, param));
+ }
+
+ /**
+ * Gets the room HF level.
+ * @return the room HF level in millibels.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getRoomHFLevel()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_ROOM_HF_LEVEL, param));
+ return byteArrayToShort(param);
+ }
+
+ /**
+ * Sets the time taken for the level of reverberation to decay by 60 dB.
+ * @param decayTime decay time in milliseconds. The valid range is [100, 20000].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setDecayTime(int decayTime)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = intToByteArray(decayTime);
+ checkStatus(setParameter(PARAM_DECAY_TIME, param));
+ }
+
+ /**
+ * Gets the decay time.
+ * @return the decay time in milliseconds.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public int getDecayTime()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[4];
+ checkStatus(getParameter(PARAM_DECAY_TIME, param));
+ return byteArrayToInt(param);
+ }
+
+ /**
+ * Sets the ratio of high frequency decay time (at 5 kHz) relative to the decay time at low
+ * frequencies.
+ * @param decayHFRatio high frequency decay ratio using a permille scale. The valid range is
+ * [100, 2000]. A ratio of 1000 indicates that all frequencies decay at the same rate.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setDecayHFRatio(short decayHFRatio)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(decayHFRatio);
+ checkStatus(setParameter(PARAM_DECAY_HF_RATIO, param));
+ }
+
+ /**
+ * Gets the ratio of high frequency decay time (at 5 kHz) relative to low frequencies.
+ * @return the decay HF ration. See {@link #setDecayHFRatio(short)} for units.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getDecayHFRatio()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_DECAY_HF_RATIO, param));
+ return byteArrayToShort(param);
+ }
+
+ /**
+ * Sets the volume level of the early reflections.
+ * <p>This level is combined with the overall room level
+ * (set using {@link #setRoomLevel(short)}).
+ * @param reflectionsLevel reflection level in millibels. The valid range is [-9000, 1000].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setReflectionsLevel(short reflectionsLevel)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(reflectionsLevel);
+ checkStatus(setParameter(PARAM_REFLECTIONS_LEVEL, param));
+ }
+
+ /**
+ * Gets the volume level of the early reflections.
+ * @return the early reflections level in millibels.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getReflectionsLevel()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_REFLECTIONS_LEVEL, param));
+ return byteArrayToShort(param);
+ }
+
+ /**
+ * Sets the delay time for the early reflections.
+ * <p>This method sets the time between when the direct path is heard and when the first
+ * reflection is heard.
+ * @param reflectionsDelay reflections delay in milliseconds. The valid range is [0, 300].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setReflectionsDelay(int reflectionsDelay)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = intToByteArray(reflectionsDelay);
+ checkStatus(setParameter(PARAM_REFLECTIONS_DELAY, param));
+ }
+
+ /**
+ * Gets the reflections delay.
+ * @return the early reflections delay in milliseconds.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public int getReflectionsDelay()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[4];
+ checkStatus(getParameter(PARAM_REFLECTIONS_DELAY, param));
+ return byteArrayToInt(param);
+ }
+
+ /**
+ * Sets the volume level of the late reverberation.
+ * <p>This level is combined with the overall room level (set using {@link #setRoomLevel(short)}).
+ * @param reverbLevel reverb level in millibels. The valid range is [-9000, 2000].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setReverbLevel(short reverbLevel)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(reverbLevel);
+ checkStatus(setParameter(PARAM_REVERB_LEVEL, param));
+ }
+
+ /**
+ * Gets the reverb level.
+ * @return the reverb level in millibels.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getReverbLevel()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_REVERB_LEVEL, param));
+ return byteArrayToShort(param);
+ }
+
+ /**
+ * Sets the time between the first reflection and the reverberation.
+ * @param reverbDelay reverb delay in milliseconds. The valid range is [0, 100].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setReverbDelay(int reverbDelay)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = intToByteArray(reverbDelay);
+ checkStatus(setParameter(PARAM_REVERB_DELAY, param));
+ }
+
+ /**
+ * Gets the reverb delay.
+ * @return the reverb delay in milliseconds.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public int getReverbDelay()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[4];
+ checkStatus(getParameter(PARAM_REVERB_DELAY, param));
+ return byteArrayToInt(param);
+ }
+
+ /**
+ * Sets the echo density in the late reverberation decay.
+ * <p>The scale should approximately map linearly to the perceived change in reverberation.
+ * @param diffusion diffusion specified using a permille scale. The diffusion valid range is
+ * [0, 1000]. A value of 1000 o/oo indicates a smooth reverberation decay.
+ * Values below this level give a more <i>grainy</i> character.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setDiffusion(short diffusion)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(diffusion);
+ checkStatus(setParameter(PARAM_DIFFUSION, param));
+ }
+
+ /**
+ * Gets diffusion level.
+ * @return the diffusion level. See {@link #setDiffusion(short)} for units.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getDiffusion()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_DIFFUSION, param));
+ return byteArrayToShort(param);
+ }
+
+
+ /**
+ * Controls the modal density of the late reverberation decay.
+ * <p> The scale should approximately map linearly to the perceived change in reverberation.
+ * A lower density creates a hollow sound that is useful for simulating small reverberation
+ * spaces such as bathrooms.
+ * @param density density specified using a permille scale. The valid range is [0, 1000].
+ * A value of 1000 o/oo indicates a natural sounding reverberation. Values below this level
+ * produce a more colored effect.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setDensity(short density)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = shortToByteArray(density);
+ checkStatus(setParameter(PARAM_DENSITY, param));
+ }
+
+ /**
+ * Gets the density level.
+ * @return the density level. See {@link #setDiffusion(short)} for units.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getDensity()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[2];
+ checkStatus(getParameter(PARAM_DENSITY, param));
+ return byteArrayToShort(param);
+ }
+
+
+ /**
+ * The OnParameterChangeListener interface defines a method called by the EnvironmentalReverb
+ * when a parameter value has changed.
+ */
+ public interface OnParameterChangeListener {
+ /**
+ * Method called when a parameter value has changed. The method is called only if the
+ * parameter was changed by another application having the control of the same
+ * EnvironmentalReverb engine.
+ * @param effect the EnvironmentalReverb on which the interface is registered.
+ * @param status status of the set parameter operation.
+ * @param param ID of the modified parameter. See {@link #PARAM_ROOM_LEVEL} ...
+ * @param value the new parameter value.
+ */
+ void onParameterChange(EnvironmentalReverb effect, int status, int param, int value);
+ }
+
+ /**
+ * Listener used internally to receive unformatted parameter change events from AudioEffect
+ * super class.
+ */
+ private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
+ private BaseParameterListener() {
+
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ OnParameterChangeListener l = null;
+
+ synchronized (mParamListenerLock) {
+ if (mParamListener != null) {
+ l = mParamListener;
+ }
+ }
+ if (l != null) {
+ int p = -1;
+ int v = -1;
+
+ if (param.length == 4) {
+ p = byteArrayToInt(param, 0);
+ }
+ if (value.length == 2) {
+ v = (int)byteArrayToShort(value, 0);
+ } else if (value.length == 4) {
+ v = byteArrayToInt(value, 0);
+ }
+ if (p != -1 && v != -1) {
+ l.onParameterChange(EnvironmentalReverb.this, status, p, v);
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers an OnParameterChangeListener interface.
+ * @param listener OnParameterChangeListener interface registered
+ */
+ public void setParameterListener(OnParameterChangeListener listener) {
+ synchronized (mParamListenerLock) {
+ if (mParamListener == null) {
+ mParamListener = listener;
+ mBaseParamListener = new BaseParameterListener();
+ super.setParameterListener(mBaseParamListener);
+ }
+ }
+ }
+
+ /**
+ * The Settings class regroups all environmental reverb parameters. It is used in
+ * conjuntion with getProperties() and setProperties() methods to backup and restore
+ * all parameters in a single call.
+ */
+ public static class Settings {
+ public short roomLevel;
+ public short roomHFLevel;
+ public int decayTime;
+ public short decayHFRatio;
+ public short reflectionsLevel;
+ public int reflectionsDelay;
+ public short reverbLevel;
+ public int reverbDelay;
+ public short diffusion;
+ public short density;
+
+ public Settings() {
+ }
+
+ /**
+ * Settings class constructor from a key=value; pairs formatted string. The string is
+ * typically returned by Settings.toString() method.
+ * @throws IllegalArgumentException if the string is not correctly formatted.
+ */
+ public Settings(String settings) {
+ StringTokenizer st = new StringTokenizer(settings, "=;");
+ int tokens = st.countTokens();
+ if (st.countTokens() != 21) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ String key = st.nextToken();
+ if (!key.equals("EnvironmentalReverb")) {
+ throw new IllegalArgumentException(
+ "invalid settings for EnvironmentalReverb: " + key);
+ }
+
+ try {
+ key = st.nextToken();
+ if (!key.equals("roomLevel")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ roomLevel = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("roomHFLevel")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ roomHFLevel = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("decayTime")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ decayTime = Integer.parseInt(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("decayHFRatio")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ decayHFRatio = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("reflectionsLevel")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ reflectionsLevel = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("reflectionsDelay")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ reflectionsDelay = Integer.parseInt(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("reverbLevel")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ reverbLevel = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("reverbDelay")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ reverbDelay = Integer.parseInt(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("diffusion")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ diffusion = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("density")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ density = Short.parseShort(st.nextToken());
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("invalid value for key: " + key);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return new String (
+ "EnvironmentalReverb"+
+ ";roomLevel="+Short.toString(roomLevel)+
+ ";roomHFLevel="+Short.toString(roomHFLevel)+
+ ";decayTime="+Integer.toString(decayTime)+
+ ";decayHFRatio="+Short.toString(decayHFRatio)+
+ ";reflectionsLevel="+Short.toString(reflectionsLevel)+
+ ";reflectionsDelay="+Integer.toString(reflectionsDelay)+
+ ";reverbLevel="+Short.toString(reverbLevel)+
+ ";reverbDelay="+Integer.toString(reverbDelay)+
+ ";diffusion="+Short.toString(diffusion)+
+ ";density="+Short.toString(density)
+ );
+ }
+ };
+
+ // Keep this in sync with sizeof(s_reverb_settings) defined in
+ // frameworks/base/include/media/EffectEnvironmentalReverbApi.h
+ static private int PROPERTY_SIZE = 26;
+
+ /**
+ * Gets the environmental reverb properties. This method is useful when a snapshot of current
+ * reverb settings must be saved by the application.
+ * @return an EnvironmentalReverb.Settings object containing all current parameters values
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public EnvironmentalReverb.Settings getProperties()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[PROPERTY_SIZE];
+ checkStatus(getParameter(PARAM_PROPERTIES, param));
+ Settings settings = new Settings();
+ settings.roomLevel = byteArrayToShort(param, 0);
+ settings.roomHFLevel = byteArrayToShort(param, 2);
+ settings.decayTime = byteArrayToInt(param, 4);
+ settings.decayHFRatio = byteArrayToShort(param, 8);
+ settings.reflectionsLevel = byteArrayToShort(param, 10);
+ settings.reflectionsDelay = byteArrayToInt(param, 12);
+ settings.reverbLevel = byteArrayToShort(param, 16);
+ settings.reverbDelay = byteArrayToInt(param, 18);
+ settings.diffusion = byteArrayToShort(param, 22);
+ settings.density = byteArrayToShort(param, 24);
+ return settings;
+ }
+
+ /**
+ * Sets the environmental reverb properties. This method is useful when reverb settings have to
+ * be applied from a previous backup.
+ * @param settings a EnvironmentalReverb.Settings object containing the properties to apply
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setProperties(EnvironmentalReverb.Settings settings)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+
+ byte[] param = concatArrays(shortToByteArray(settings.roomLevel),
+ shortToByteArray(settings.roomHFLevel),
+ intToByteArray(settings.decayTime),
+ shortToByteArray(settings.decayHFRatio),
+ shortToByteArray(settings.reflectionsLevel),
+ intToByteArray(settings.reflectionsDelay),
+ shortToByteArray(settings.reverbLevel),
+ intToByteArray(settings.reverbDelay),
+ shortToByteArray(settings.diffusion),
+ shortToByteArray(settings.density));
+
+ checkStatus(setParameter(PARAM_PROPERTIES, param));
+ }
+}
diff --git a/android/media/audiofx/Equalizer.java b/android/media/audiofx/Equalizer.java
new file mode 100644
index 00000000..7abada07
--- /dev/null
+++ b/android/media/audiofx/Equalizer.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.media.audiofx.AudioEffect;
+import android.util.Log;
+
+import java.util.StringTokenizer;
+
+
+/**
+ * An Equalizer is used to alter the frequency response of a particular music source or of the main
+ * output mix.
+ * <p>An application creates an Equalizer object to instantiate and control an Equalizer engine
+ * in the audio framework. The application can either simply use predefined presets or have a more
+ * precise control of the gain in each frequency band controlled by the equalizer.
+ * <p>The methods, parameter types and units exposed by the Equalizer implementation are directly
+ * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/)
+ * for the SLEqualizerItf interface. Please refer to this specification for more details.
+ * <p>To attach the Equalizer to a particular AudioTrack or MediaPlayer, specify the audio session
+ * ID of this AudioTrack or MediaPlayer when constructing the Equalizer.
+ * <p>NOTE: attaching an Equalizer to the global audio output mix by use of session 0 is deprecated.
+ * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling audio
+ * effects.
+ */
+
+public class Equalizer extends AudioEffect {
+
+ private final static String TAG = "Equalizer";
+
+ // These constants must be synchronized with those in
+ // frameworks/base/include/media/EffectEqualizerApi.h
+ /**
+ * Number of bands. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_NUM_BANDS = 0;
+ /**
+ * Band level range. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_LEVEL_RANGE = 1;
+ /**
+ * Band level. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_BAND_LEVEL = 2;
+ /**
+ * Band center frequency. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_CENTER_FREQ = 3;
+ /**
+ * Band frequency range. Parameter ID for
+ * {@link android.media.audiofx.Equalizer.OnParameterChangeListener}
+ */
+ public static final int PARAM_BAND_FREQ_RANGE = 4;
+ /**
+ * Band for a given frequency. Parameter ID for OnParameterChangeListener
+ *
+ */
+ public static final int PARAM_GET_BAND = 5;
+ /**
+ * Current preset. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_CURRENT_PRESET = 6;
+ /**
+ * Request number of presets. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_GET_NUM_OF_PRESETS = 7;
+ /**
+ * Request preset name. Parameter ID for OnParameterChangeListener
+ */
+ public static final int PARAM_GET_PRESET_NAME = 8;
+ // used by setProperties()/getProperties
+ private static final int PARAM_PROPERTIES = 9;
+ /**
+ * Maximum size for preset name
+ */
+ public static final int PARAM_STRING_SIZE_MAX = 32;
+
+ /**
+ * Number of bands implemented by Equalizer engine
+ */
+ private short mNumBands = 0;
+
+ /**
+ * Number of presets implemented by Equalizer engine
+ */
+ private int mNumPresets;
+ /**
+ * Names of presets implemented by Equalizer engine
+ */
+ private String[] mPresetNames;
+
+ /**
+ * Registered listener for parameter changes.
+ */
+ private OnParameterChangeListener mParamListener = null;
+
+ /**
+ * Listener used internally to to receive raw parameter change event from AudioEffect super class
+ */
+ private BaseParameterListener mBaseParamListener = null;
+
+ /**
+ * Lock for access to mParamListener
+ */
+ private final Object mParamListenerLock = new Object();
+
+ /**
+ * Class constructor.
+ * @param priority the priority level requested by the application for controlling the Equalizer
+ * engine. As the same 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. The Equalizer will be
+ * attached to the MediaPlayer or AudioTrack in the same audio session.
+ *
+ * @throws java.lang.IllegalStateException
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ public Equalizer(int priority, int audioSession)
+ throws IllegalStateException, IllegalArgumentException,
+ UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_EQUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
+
+ if (audioSession == 0) {
+ Log.w(TAG, "WARNING: attaching an Equalizer to global output mix is deprecated!");
+ }
+
+ getNumberOfBands();
+
+ mNumPresets = (int)getNumberOfPresets();
+
+ if (mNumPresets != 0) {
+ mPresetNames = new String[mNumPresets];
+ byte[] value = new byte[PARAM_STRING_SIZE_MAX];
+ int[] param = new int[2];
+ param[0] = PARAM_GET_PRESET_NAME;
+ for (int i = 0; i < mNumPresets; i++) {
+ param[1] = i;
+ checkStatus(getParameter(param, value));
+ int length = 0;
+ while (value[length] != 0) length++;
+ try {
+ mPresetNames[i] = new String(value, 0, length, "ISO-8859-1");
+ } catch (java.io.UnsupportedEncodingException e) {
+ Log.e(TAG, "preset name decode error");
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the number of frequency bands supported by the Equalizer engine.
+ * @return the number of bands
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getNumberOfBands()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ if (mNumBands != 0) {
+ return mNumBands;
+ }
+ int[] param = new int[1];
+ param[0] = PARAM_NUM_BANDS;
+ short[] result = new short[1];
+ checkStatus(getParameter(param, result));
+ mNumBands = result[0];
+ return mNumBands;
+ }
+
+ /**
+ * Gets the level range for use by {@link #setBandLevel(short,short)}. The level is expressed in
+ * milliBel.
+ * @return the band level range in an array of short integers. The first element is the lower
+ * limit of the range, the second element the upper limit.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short[] getBandLevelRange()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ short[] result = new short[2];
+ checkStatus(getParameter(PARAM_LEVEL_RANGE, result));
+ return result;
+ }
+
+ /**
+ * Sets the given equalizer band to the given gain value.
+ * @param band frequency band that will have the new gain. The numbering of the bands starts
+ * from 0 and ends at (number of bands - 1).
+ * @param level new gain in millibels that will be set to the given band. getBandLevelRange()
+ * will define the maximum and minimum values.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ * @see #getNumberOfBands()
+ */
+ public void setBandLevel(short band, short level)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[2];
+ short[] value = new short[1];
+
+ param[0] = PARAM_BAND_LEVEL;
+ param[1] = (int)band;
+ value[0] = level;
+ checkStatus(setParameter(param, value));
+ }
+
+ /**
+ * Gets the gain set for the given equalizer band.
+ * @param band frequency band whose gain is requested. The numbering of the bands starts
+ * from 0 and ends at (number of bands - 1).
+ * @return the gain in millibels of the given band.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getBandLevel(short band)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[2];
+ short[] result = new short[1];
+
+ param[0] = PARAM_BAND_LEVEL;
+ param[1] = (int)band;
+ checkStatus(getParameter(param, result));
+
+ return result[0];
+ }
+
+
+ /**
+ * Gets the center frequency of the given band.
+ * @param band frequency band whose center frequency is requested. The numbering of the bands
+ * starts from 0 and ends at (number of bands - 1).
+ * @return the center frequency in milliHertz
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public int getCenterFreq(short band)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[2];
+ int[] result = new int[1];
+
+ param[0] = PARAM_CENTER_FREQ;
+ param[1] = (int)band;
+ checkStatus(getParameter(param, result));
+
+ return result[0];
+ }
+
+ /**
+ * Gets the frequency range of the given frequency band.
+ * @param band frequency band whose frequency range is requested. The numbering of the bands
+ * starts from 0 and ends at (number of bands - 1).
+ * @return the frequency range in millHertz in an array of integers. The first element is the
+ * lower limit of the range, the second element the upper limit.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public int[] getBandFreqRange(short band)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[2];
+ int[] result = new int[2];
+ param[0] = PARAM_BAND_FREQ_RANGE;
+ param[1] = (int)band;
+ checkStatus(getParameter(param, result));
+
+ return result;
+ }
+
+ /**
+ * Gets the band that has the most effect on the given frequency.
+ * @param frequency frequency in milliHertz which is to be equalized via the returned band.
+ * @return the frequency band that has most effect on the given frequency.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getBand(int frequency)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] param = new int[2];
+ short[] result = new short[1];
+
+ param[0] = PARAM_GET_BAND;
+ param[1] = frequency;
+ checkStatus(getParameter(param, result));
+
+ return result[0];
+ }
+
+ /**
+ * Gets current preset.
+ * @return the preset that is set at the moment.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getCurrentPreset()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ short[] result = new short[1];
+ checkStatus(getParameter(PARAM_CURRENT_PRESET, result));
+ return result[0];
+ }
+
+ /**
+ * Sets the equalizer according to the given preset.
+ * @param preset new preset that will be taken into use. The valid range is [0,
+ * number of presets-1].
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ * @see #getNumberOfPresets()
+ */
+ public void usePreset(short preset)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_CURRENT_PRESET, preset));
+ }
+
+ /**
+ * Gets the total number of presets the equalizer supports. The presets will have indices
+ * [0, number of presets-1].
+ * @return the number of presets the equalizer supports.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getNumberOfPresets()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ short[] result = new short[1];
+ checkStatus(getParameter(PARAM_GET_NUM_OF_PRESETS, result));
+ return result[0];
+ }
+
+ /**
+ * Gets the preset name based on the index.
+ * @param preset index of the preset. The valid range is [0, number of presets-1].
+ * @return a string containing the name of the given preset.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public String getPresetName(short preset)
+ {
+ if (preset >= 0 && preset < mNumPresets) {
+ return mPresetNames[preset];
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * The OnParameterChangeListener interface defines a method called by the Equalizer when a
+ * parameter value has changed.
+ */
+ public interface OnParameterChangeListener {
+ /**
+ * Method called when a parameter value has changed. The method is called only if the
+ * parameter was changed by another application having the control of the same
+ * Equalizer engine.
+ * @param effect the Equalizer on which the interface is registered.
+ * @param status status of the set parameter operation.
+ * @param param1 ID of the modified parameter. See {@link #PARAM_BAND_LEVEL} ...
+ * @param param2 additional parameter qualifier (e.g the band for band level parameter).
+ * @param value the new parameter value.
+ */
+ void onParameterChange(Equalizer effect, int status, int param1, int param2, int value);
+ }
+
+ /**
+ * Listener used internally to receive unformatted parameter change events from AudioEffect
+ * super class.
+ */
+ private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
+ private BaseParameterListener() {
+
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ OnParameterChangeListener l = null;
+
+ synchronized (mParamListenerLock) {
+ if (mParamListener != null) {
+ l = mParamListener;
+ }
+ }
+ if (l != null) {
+ int p1 = -1;
+ int p2 = -1;
+ int v = -1;
+
+ if (param.length >= 4) {
+ p1 = byteArrayToInt(param, 0);
+ if (param.length >= 8) {
+ p2 = byteArrayToInt(param, 4);
+ }
+ }
+ if (value.length == 2) {
+ v = (int)byteArrayToShort(value, 0);;
+ } else if (value.length == 4) {
+ v = byteArrayToInt(value, 0);
+ }
+
+ if (p1 != -1 && v != -1) {
+ l.onParameterChange(Equalizer.this, status, p1, p2, v);
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers an OnParameterChangeListener interface.
+ * @param listener OnParameterChangeListener interface registered
+ */
+ public void setParameterListener(OnParameterChangeListener listener) {
+ synchronized (mParamListenerLock) {
+ if (mParamListener == null) {
+ mParamListener = listener;
+ mBaseParamListener = new BaseParameterListener();
+ super.setParameterListener(mBaseParamListener);
+ }
+ }
+ }
+
+ /**
+ * The Settings class regroups all equalizer parameters. It is used in
+ * conjuntion with getProperties() and setProperties() methods to backup and restore
+ * all parameters in a single call.
+ */
+ public static class Settings {
+ public short curPreset;
+ public short numBands = 0;
+ public short[] bandLevels = null;
+
+ public Settings() {
+ }
+
+ /**
+ * Settings class constructor from a key=value; pairs formatted string. The string is
+ * typically returned by Settings.toString() method.
+ * @throws IllegalArgumentException if the string is not correctly formatted.
+ */
+ public Settings(String settings) {
+ StringTokenizer st = new StringTokenizer(settings, "=;");
+ int tokens = st.countTokens();
+ if (st.countTokens() < 5) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ String key = st.nextToken();
+ if (!key.equals("Equalizer")) {
+ throw new IllegalArgumentException(
+ "invalid settings for Equalizer: " + key);
+ }
+ try {
+ key = st.nextToken();
+ if (!key.equals("curPreset")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ curPreset = Short.parseShort(st.nextToken());
+ key = st.nextToken();
+ if (!key.equals("numBands")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ numBands = Short.parseShort(st.nextToken());
+ if (st.countTokens() != numBands*2) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ bandLevels = new short[numBands];
+ for (int i = 0; i < numBands; i++) {
+ key = st.nextToken();
+ if (!key.equals("band"+(i+1)+"Level")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ bandLevels[i] = Short.parseShort(st.nextToken());
+ }
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("invalid value for key: " + key);
+ }
+ }
+
+ @Override
+ public String toString() {
+
+ String str = new String (
+ "Equalizer"+
+ ";curPreset="+Short.toString(curPreset)+
+ ";numBands="+Short.toString(numBands)
+ );
+ for (int i = 0; i < numBands; i++) {
+ str = str.concat(";band"+(i+1)+"Level="+Short.toString(bandLevels[i]));
+ }
+ return str;
+ }
+ };
+
+
+ /**
+ * Gets the equalizer properties. This method is useful when a snapshot of current
+ * equalizer settings must be saved by the application.
+ * @return an Equalizer.Settings object containing all current parameters values
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public Equalizer.Settings getProperties()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ byte[] param = new byte[4 + mNumBands * 2];
+ checkStatus(getParameter(PARAM_PROPERTIES, param));
+ Settings settings = new Settings();
+ settings.curPreset = byteArrayToShort(param, 0);
+ settings.numBands = byteArrayToShort(param, 2);
+ settings.bandLevels = new short[mNumBands];
+ for (int i = 0; i < mNumBands; i++) {
+ settings.bandLevels[i] = byteArrayToShort(param, 4 + 2*i);
+ }
+ return settings;
+ }
+
+ /**
+ * Sets the equalizer properties. This method is useful when equalizer settings have to
+ * be applied from a previous backup.
+ * @param settings an Equalizer.Settings object containing the properties to apply
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setProperties(Equalizer.Settings settings)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ if (settings.numBands != settings.bandLevels.length ||
+ settings.numBands != mNumBands) {
+ throw new IllegalArgumentException("settings invalid band count: " +settings.numBands);
+ }
+
+ byte[] param = concatArrays(shortToByteArray(settings.curPreset),
+ shortToByteArray(mNumBands));
+ for (int i = 0; i < mNumBands; i++) {
+ param = concatArrays(param,
+ shortToByteArray(settings.bandLevels[i]));
+ }
+ checkStatus(setParameter(PARAM_PROPERTIES, param));
+ }
+}
diff --git a/android/media/audiofx/LoudnessEnhancer.java b/android/media/audiofx/LoudnessEnhancer.java
new file mode 100644
index 00000000..7dc41753
--- /dev/null
+++ b/android/media/audiofx/LoudnessEnhancer.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.media.AudioTrack;
+import android.media.MediaPlayer;
+import android.media.audiofx.AudioEffect;
+import android.util.Log;
+
+import java.util.StringTokenizer;
+
+
+/**
+ * LoudnessEnhancer is an audio effect for increasing audio loudness.
+ * The processing is parametrized by a target gain value, which determines the maximum amount
+ * by which an audio signal will be amplified; signals amplified outside of the sample
+ * range supported by the platform are compressed.
+ * An application creates a LoudnessEnhancer object to instantiate and control a
+ * this audio effect in the audio framework.
+ * To attach the LoudnessEnhancer to a particular AudioTrack or MediaPlayer,
+ * specify the audio session ID of this AudioTrack or MediaPlayer when constructing the effect
+ * (see {@link AudioTrack#getAudioSessionId()} and {@link MediaPlayer#getAudioSessionId()}).
+ */
+
+public class LoudnessEnhancer extends AudioEffect {
+
+ private final static String TAG = "LoudnessEnhancer";
+
+ // These parameter constants must be synchronized with those in
+ // /system/media/audio_effects/include/audio_effects/effect_loudnessenhancer.h
+ /**
+ * The maximum gain applied applied to the signal to process.
+ * It is expressed in millibels (100mB = 1dB) where 0mB corresponds to no amplification.
+ */
+ public static final int PARAM_TARGET_GAIN_MB = 0;
+
+ /**
+ * Registered listener for parameter changes.
+ */
+ private OnParameterChangeListener mParamListener = null;
+
+ /**
+ * Listener used internally to to receive raw parameter change events
+ * from AudioEffect super class
+ */
+ private BaseParameterListener mBaseParamListener = null;
+
+ /**
+ * Lock for access to mParamListener
+ */
+ private final Object mParamListenerLock = new Object();
+
+ /**
+ * Class constructor.
+ * @param audioSession system-wide unique audio session identifier. The LoudnessEnhancer
+ * will be attached to the MediaPlayer or AudioTrack in the same audio session.
+ *
+ * @throws java.lang.IllegalStateException
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ public LoudnessEnhancer(int audioSession)
+ throws IllegalStateException, IllegalArgumentException,
+ UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_LOUDNESS_ENHANCER, EFFECT_TYPE_NULL, 0, audioSession);
+
+ if (audioSession == 0) {
+ Log.w(TAG, "WARNING: attaching a LoudnessEnhancer to global output mix is deprecated!");
+ }
+ }
+
+ /**
+ * @hide
+ * Class constructor for the LoudnessEnhancer audio effect.
+ * @param priority the priority level requested by the application for controlling the
+ * LoudnessEnhancer engine. As the same 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. The LoudnessEnhancer
+ * will be attached to the MediaPlayer or AudioTrack in the same audio session.
+ *
+ * @throws java.lang.IllegalStateException
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ public LoudnessEnhancer(int priority, int audioSession)
+ throws IllegalStateException, IllegalArgumentException,
+ UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_LOUDNESS_ENHANCER, EFFECT_TYPE_NULL, priority, audioSession);
+
+ if (audioSession == 0) {
+ Log.w(TAG, "WARNING: attaching a LoudnessEnhancer to global output mix is deprecated!");
+ }
+ }
+
+ /**
+ * Set the target gain for the audio effect.
+ * The target gain is the maximum value by which a sample value will be amplified when the
+ * effect is enabled.
+ * @param gainmB the effect target gain expressed in mB. 0mB corresponds to no amplification.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setTargetGain(int gainmB)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_TARGET_GAIN_MB, gainmB));
+ }
+
+ /**
+ * Return the target gain.
+ * @return the effect target gain expressed in mB.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public float getTargetGain()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ int[] value = new int[1];
+ checkStatus(getParameter(PARAM_TARGET_GAIN_MB, value));
+ return value[0];
+ }
+
+ /**
+ * @hide
+ * The OnParameterChangeListener interface defines a method called by the LoudnessEnhancer
+ * when a parameter value has changed.
+ */
+ public interface OnParameterChangeListener {
+ /**
+ * Method called when a parameter value has changed. The method is called only if the
+ * parameter was changed by another application having the control of the same
+ * LoudnessEnhancer engine.
+ * @param effect the LoudnessEnhancer on which the interface is registered.
+ * @param param ID of the modified parameter. See {@link #PARAM_GENERIC_PARAM1} ...
+ * @param value the new parameter value.
+ */
+ void onParameterChange(LoudnessEnhancer effect, int param, int value);
+ }
+
+ /**
+ * Listener used internally to receive unformatted parameter change events from AudioEffect
+ * super class.
+ */
+ private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
+ private BaseParameterListener() {
+
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ // only notify when the parameter was successfully change
+ if (status != AudioEffect.SUCCESS) {
+ return;
+ }
+ OnParameterChangeListener l = null;
+ synchronized (mParamListenerLock) {
+ if (mParamListener != null) {
+ l = mParamListener;
+ }
+ }
+ if (l != null) {
+ int p = -1;
+ int v = Integer.MIN_VALUE;
+
+ if (param.length == 4) {
+ p = byteArrayToInt(param, 0);
+ }
+ if (value.length == 4) {
+ v = byteArrayToInt(value, 0);
+ }
+ if (p != -1 && v != Integer.MIN_VALUE) {
+ l.onParameterChange(LoudnessEnhancer.this, p, v);
+ }
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Registers an OnParameterChangeListener interface.
+ * @param listener OnParameterChangeListener interface registered
+ */
+ public void setParameterListener(OnParameterChangeListener listener) {
+ synchronized (mParamListenerLock) {
+ if (mParamListener == null) {
+ mBaseParamListener = new BaseParameterListener();
+ super.setParameterListener(mBaseParamListener);
+ }
+ mParamListener = listener;
+ }
+ }
+
+ /**
+ * @hide
+ * The Settings class regroups the LoudnessEnhancer parameters. It is used in
+ * conjunction with the getProperties() and setProperties() methods to backup and restore
+ * all parameters in a single call.
+ */
+ public static class Settings {
+ public int targetGainmB;
+
+ public Settings() {
+ }
+
+ /**
+ * Settings class constructor from a key=value; pairs formatted string. The string is
+ * typically returned by Settings.toString() method.
+ * @throws IllegalArgumentException if the string is not correctly formatted.
+ */
+ public Settings(String settings) {
+ StringTokenizer st = new StringTokenizer(settings, "=;");
+ //int tokens = st.countTokens();
+ if (st.countTokens() != 3) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ String key = st.nextToken();
+ if (!key.equals("LoudnessEnhancer")) {
+ throw new IllegalArgumentException(
+ "invalid settings for LoudnessEnhancer: " + key);
+ }
+ try {
+ key = st.nextToken();
+ if (!key.equals("targetGainmB")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ targetGainmB = Integer.parseInt(st.nextToken());
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("invalid value for key: " + key);
+ }
+ }
+
+ @Override
+ public String toString() {
+ String str = new String (
+ "LoudnessEnhancer"+
+ ";targetGainmB="+Integer.toString(targetGainmB)
+ );
+ return str;
+ }
+ };
+
+
+ /**
+ * @hide
+ * Gets the LoudnessEnhancer properties. This method is useful when a snapshot of current
+ * effect settings must be saved by the application.
+ * @return a LoudnessEnhancer.Settings object containing all current parameters values
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public LoudnessEnhancer.Settings getProperties()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ Settings settings = new Settings();
+ int[] value = new int[1];
+ checkStatus(getParameter(PARAM_TARGET_GAIN_MB, value));
+ settings.targetGainmB = value[0];
+ return settings;
+ }
+
+ /**
+ * @hide
+ * Sets the LoudnessEnhancer properties. This method is useful when bass boost settings
+ * have to be applied from a previous backup.
+ * @param settings a LoudnessEnhancer.Settings object containing the properties to apply
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setProperties(LoudnessEnhancer.Settings settings)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_TARGET_GAIN_MB, settings.targetGainmB));
+ }
+}
diff --git a/android/media/audiofx/NoiseSuppressor.java b/android/media/audiofx/NoiseSuppressor.java
new file mode 100644
index 00000000..70cc87cc
--- /dev/null
+++ b/android/media/audiofx/NoiseSuppressor.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.util.Log;
+
+/**
+ * Noise Suppressor (NS).
+ * <p>Noise suppression (NS) is an audio pre-processor which removes background noise from the
+ * captured signal. The component of the signal considered as noise can be either stationary
+ * (car/airplane engine, AC system) or non-stationary (other peoples conversations, car horn) for
+ * more advanced implementations.
+ * <p>NS is mostly used by voice communication applications (voice chat, video conferencing,
+ * SIP calls).
+ * <p>An application creates a NoiseSuppressor object to instantiate and control an NS
+ * engine in the audio framework.
+ * <p>To attach the NoiseSuppressor to a particular {@link android.media.AudioRecord},
+ * specify the audio session ID of this AudioRecord when creating the NoiseSuppressor.
+ * The audio session is retrieved by calling
+ * {@link android.media.AudioRecord#getAudioSessionId()} on the AudioRecord instance.
+ * <p>On some devices, NS can be inserted by default in the capture path by the platform
+ * according to the {@link android.media.MediaRecorder.AudioSource} used. The application should
+ * call NoiseSuppressor.getEnable() after creating the NS to check the default NS activation
+ * state on a particular AudioRecord session.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on
+ * controlling audio effects.
+ */
+
+public class NoiseSuppressor extends AudioEffect {
+
+ private final static String TAG = "NoiseSuppressor";
+
+ /**
+ * Checks if the device implements noise suppression.
+ * @return true if the device implements noise suppression, false otherwise.
+ */
+ public static boolean isAvailable() {
+ return AudioEffect.isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_NS);
+ }
+
+ /**
+ * Creates a NoiseSuppressor and attaches it to the AudioRecord on the audio
+ * session specified.
+ * @param audioSession system wide unique audio session identifier. The NoiseSuppressor
+ * will be applied to the AudioRecord with the same audio session.
+ * @return NoiseSuppressor created or null if the device does not implement noise
+ * suppression.
+ */
+ public static NoiseSuppressor create(int audioSession) {
+ NoiseSuppressor ns = null;
+ try {
+ ns = new NoiseSuppressor(audioSession);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "not implemented on this device "+ns);
+ } catch (UnsupportedOperationException e) {
+ Log.w(TAG, "not enough resources");
+ } catch (RuntimeException e) {
+ Log.w(TAG, "not enough memory");
+ }
+ return ns;
+ }
+
+ /**
+ * Class constructor.
+ * <p> The constructor is not guarantied to succeed and throws the following exceptions:
+ * <ul>
+ * <li>IllegalArgumentException is thrown if the device does not implement an NS</li>
+ * <li>UnsupportedOperationException is thrown is the resources allocated to audio
+ * pre-procesing are currently exceeded.</li>
+ * <li>RuntimeException is thrown if a memory allocation error occurs.</li>
+ * </ul>
+ *
+ * @param audioSession system wide unique audio session identifier. The NoiseSuppressor
+ * will be applied to the AudioRecord with the same audio session.
+ *
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ private NoiseSuppressor(int audioSession)
+ throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_NS, EFFECT_TYPE_NULL, 0, audioSession);
+ }
+}
diff --git a/android/media/audiofx/PresetReverb.java b/android/media/audiofx/PresetReverb.java
new file mode 100644
index 00000000..ef916678
--- /dev/null
+++ b/android/media/audiofx/PresetReverb.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.media.audiofx.AudioEffect;
+import java.util.StringTokenizer;
+
+
+/**
+ * A sound generated within a room travels in many directions. The listener first hears the
+ * direct sound from the source itself. Later, he or she hears discrete echoes caused by sound
+ * bouncing off nearby walls, the ceiling and the floor. As sound waves arrive after
+ * undergoing more and more reflections, individual reflections become indistinguishable and
+ * the listener hears continuous reverberation that decays over time.
+ * Reverb is vital for modeling a listener's environment. It can be used in music applications
+ * to simulate music being played back in various environments, or in games to immerse the
+ * listener within the game's environment.
+ * The PresetReverb class allows an application to configure the global reverb using a reverb preset.
+ * This is primarily used for adding some reverb in a music playback context. Applications
+ * requiring control over a more advanced environmental reverb are advised to use the
+ * {@link android.media.audiofx.EnvironmentalReverb} class.
+ * <p>An application creates a PresetReverb object to instantiate and control a reverb engine in the
+ * audio framework.
+ * <p>The methods, parameter types and units exposed by the PresetReverb implementation are
+ * directly mapping those defined by the OpenSL ES 1.0.1 Specification
+ * (http://www.khronos.org/opensles/) for the SLPresetReverbItf interface.
+ * Please refer to this specification for more details.
+ * <p>The PresetReverb is an output mix auxiliary effect and should be created on
+ * Audio session 0. In order for a MediaPlayer or AudioTrack to be fed into this effect,
+ * they must be explicitely attached to it and a send level must be specified. Use the effect ID
+ * returned by getId() method to designate this particular effect when attaching it to the
+ * MediaPlayer or AudioTrack.
+ * <p>Creating a reverb on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
+ * audio effects.
+ */
+
+public class PresetReverb extends AudioEffect {
+
+ private final static String TAG = "PresetReverb";
+
+ // These constants must be synchronized with those in
+ // frameworks/base/include/media/EffectPresetReverbApi.h
+
+ /**
+ * Preset. Parameter ID for
+ * {@link android.media.audiofx.PresetReverb.OnParameterChangeListener}
+ */
+ public static final int PARAM_PRESET = 0;
+
+ /**
+ * No reverb or reflections
+ */
+ public static final short PRESET_NONE = 0;
+ /**
+ * Reverb preset representing a small room less than five meters in length
+ */
+ public static final short PRESET_SMALLROOM = 1;
+ /**
+ * Reverb preset representing a medium room with a length of ten meters or less
+ */
+ public static final short PRESET_MEDIUMROOM = 2;
+ /**
+ * Reverb preset representing a large-sized room suitable for live performances
+ */
+ public static final short PRESET_LARGEROOM = 3;
+ /**
+ * Reverb preset representing a medium-sized hall
+ */
+ public static final short PRESET_MEDIUMHALL = 4;
+ /**
+ * Reverb preset representing a large-sized hall suitable for a full orchestra
+ */
+ public static final short PRESET_LARGEHALL = 5;
+ /**
+ * Reverb preset representing a synthesis of the traditional plate reverb
+ */
+ public static final short PRESET_PLATE = 6;
+
+ /**
+ * Registered listener for parameter changes.
+ */
+ private OnParameterChangeListener mParamListener = null;
+
+ /**
+ * Listener used internally to to receive raw parameter change event from AudioEffect super class
+ */
+ private BaseParameterListener mBaseParamListener = null;
+
+ /**
+ * Lock for access to mParamListener
+ */
+ private final Object mParamListenerLock = new Object();
+
+ /**
+ * Class constructor.
+ * @param priority the priority level requested by the application for controlling the
+ * PresetReverb engine. As the same 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 PresetReverb will be attached to the MediaPlayer or AudioTrack in the
+ * same audio session. Otherwise, the PresetReverb will apply to the output mix.
+ * As the PresetReverb is an auxiliary effect it is recommended to instantiate it on
+ * audio session 0 and to attach it to the MediaPLayer auxiliary output.
+ *
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ public PresetReverb(int priority, int audioSession)
+ throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_PRESET_REVERB, EFFECT_TYPE_NULL, priority, audioSession);
+ }
+
+ /**
+ * Enables a preset on the reverb.
+ * <p>The reverb PRESET_NONE disables any reverb from the current output but does not free the
+ * resources associated with the reverb. For an application to signal to the implementation
+ * to free the resources, it must call the release() method.
+ * @param preset this must be one of the the preset constants defined in this class.
+ * e.g. {@link #PRESET_SMALLROOM}
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setPreset(short preset)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_PRESET, preset));
+ }
+
+ /**
+ * Gets current reverb preset.
+ * @return the preset that is set at the moment.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getPreset()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ short[] value = new short[1];
+ checkStatus(getParameter(PARAM_PRESET, value));
+ return value[0];
+ }
+
+ /**
+ * The OnParameterChangeListener interface defines a method called by the PresetReverb
+ * when a parameter value has changed.
+ */
+ public interface OnParameterChangeListener {
+ /**
+ * Method called when a parameter value has changed. The method is called only if the
+ * parameter was changed by another application having the control of the same
+ * PresetReverb engine.
+ * @param effect the PresetReverb on which the interface is registered.
+ * @param status status of the set parameter operation.
+ * @param param ID of the modified parameter. See {@link #PARAM_PRESET} ...
+ * @param value the new parameter value.
+ */
+ void onParameterChange(PresetReverb effect, int status, int param, short value);
+ }
+
+ /**
+ * Listener used internally to receive unformatted parameter change events from AudioEffect
+ * super class.
+ */
+ private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
+ private BaseParameterListener() {
+
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ OnParameterChangeListener l = null;
+
+ synchronized (mParamListenerLock) {
+ if (mParamListener != null) {
+ l = mParamListener;
+ }
+ }
+ if (l != null) {
+ int p = -1;
+ short v = -1;
+
+ if (param.length == 4) {
+ p = byteArrayToInt(param, 0);
+ }
+ if (value.length == 2) {
+ v = byteArrayToShort(value, 0);
+ }
+ if (p != -1 && v != -1) {
+ l.onParameterChange(PresetReverb.this, status, p, v);
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers an OnParameterChangeListener interface.
+ * @param listener OnParameterChangeListener interface registered
+ */
+ public void setParameterListener(OnParameterChangeListener listener) {
+ synchronized (mParamListenerLock) {
+ if (mParamListener == null) {
+ mParamListener = listener;
+ mBaseParamListener = new BaseParameterListener();
+ super.setParameterListener(mBaseParamListener);
+ }
+ }
+ }
+
+ /**
+ * The Settings class regroups all preset reverb parameters. It is used in
+ * conjuntion with getProperties() and setProperties() methods to backup and restore
+ * all parameters in a single call.
+ */
+ public static class Settings {
+ public short preset;
+
+ public Settings() {
+ }
+
+ /**
+ * Settings class constructor from a key=value; pairs formatted string. The string is
+ * typically returned by Settings.toString() method.
+ * @throws IllegalArgumentException if the string is not correctly formatted.
+ */
+ public Settings(String settings) {
+ StringTokenizer st = new StringTokenizer(settings, "=;");
+ int tokens = st.countTokens();
+ if (st.countTokens() != 3) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ String key = st.nextToken();
+ if (!key.equals("PresetReverb")) {
+ throw new IllegalArgumentException(
+ "invalid settings for PresetReverb: " + key);
+ }
+ try {
+ key = st.nextToken();
+ if (!key.equals("preset")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ preset = Short.parseShort(st.nextToken());
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("invalid value for key: " + key);
+ }
+ }
+
+ @Override
+ public String toString() {
+ String str = new String (
+ "PresetReverb"+
+ ";preset="+Short.toString(preset)
+ );
+ return str;
+ }
+ };
+
+
+ /**
+ * Gets the preset reverb properties. This method is useful when a snapshot of current
+ * preset reverb settings must be saved by the application.
+ * @return a PresetReverb.Settings object containing all current parameters values
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public PresetReverb.Settings getProperties()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ Settings settings = new Settings();
+ short[] value = new short[1];
+ checkStatus(getParameter(PARAM_PRESET, value));
+ settings.preset = value[0];
+ return settings;
+ }
+
+ /**
+ * Sets the preset reverb properties. This method is useful when preset reverb settings have to
+ * be applied from a previous backup.
+ * @param settings a PresetReverb.Settings object containing the properties to apply
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setProperties(PresetReverb.Settings settings)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_PRESET, settings.preset));
+ }
+}
diff --git a/android/media/audiofx/Virtualizer.java b/android/media/audiofx/Virtualizer.java
new file mode 100644
index 00000000..74b6fc13
--- /dev/null
+++ b/android/media/audiofx/Virtualizer.java
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.annotation.IntDef;
+import android.media.AudioDeviceInfo;
+import android.media.AudioFormat;
+import android.media.audiofx.AudioEffect;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.StringTokenizer;
+
+
+/**
+ * An audio virtualizer is a general name for an effect to spatialize audio channels. The exact
+ * behavior of this effect is dependent on the number of audio input channels and the types and
+ * number of audio output channels of the device. For example, in the case of a stereo input and
+ * stereo headphone output, a stereo widening effect is used when this effect is turned on.
+ * <p>An application creates a Virtualizer object to instantiate and control a virtualizer engine
+ * in the audio framework.
+ * <p>The methods, parameter types and units exposed by the Virtualizer implementation are directly
+ * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/)
+ * for the SLVirtualizerItf interface. Please refer to this specification for more details.
+ * <p>To attach the Virtualizer to a particular AudioTrack or MediaPlayer, specify the audio session
+ * ID of this AudioTrack or MediaPlayer when constructing the Virtualizer.
+ * <p>NOTE: attaching a Virtualizer to the global audio output mix by use of session 0 is
+ * deprecated.
+ * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
+ * audio effects.
+ */
+
+public class Virtualizer extends AudioEffect {
+
+ private final static String TAG = "Virtualizer";
+ private final static boolean DEBUG = false;
+
+ // These constants must be synchronized with those in
+ // system/media/audio_effects/include/audio_effects/effect_virtualizer.h
+ /**
+ * Is strength parameter supported by virtualizer engine. Parameter ID for getParameter().
+ */
+ public static final int PARAM_STRENGTH_SUPPORTED = 0;
+ /**
+ * Virtualizer effect strength. Parameter ID for
+ * {@link android.media.audiofx.Virtualizer.OnParameterChangeListener}
+ */
+ public static final int PARAM_STRENGTH = 1;
+ /**
+ * @hide
+ * Parameter ID to query the virtual speaker angles for a channel mask / device configuration.
+ */
+ public static final int PARAM_VIRTUAL_SPEAKER_ANGLES = 2;
+ /**
+ * @hide
+ * Parameter ID to force the virtualization mode to be that of a specific device
+ */
+ public static final int PARAM_FORCE_VIRTUALIZATION_MODE = 3;
+ /**
+ * @hide
+ * Parameter ID to query the current virtualization mode.
+ */
+ public static final int PARAM_VIRTUALIZATION_MODE = 4;
+
+ /**
+ * Indicates if strength parameter is supported by the virtualizer engine
+ */
+ private boolean mStrengthSupported = false;
+
+ /**
+ * Registered listener for parameter changes.
+ */
+ private OnParameterChangeListener mParamListener = null;
+
+ /**
+ * Listener used internally to to receive raw parameter change event from AudioEffect super class
+ */
+ private BaseParameterListener mBaseParamListener = null;
+
+ /**
+ * Lock for access to mParamListener
+ */
+ private final Object mParamListenerLock = new Object();
+
+ /**
+ * Class constructor.
+ * @param priority the priority level requested by the application for controlling the Virtualizer
+ * engine. As the same 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. The Virtualizer will
+ * be attached to the MediaPlayer or AudioTrack in the same audio session.
+ *
+ * @throws java.lang.IllegalStateException
+ * @throws java.lang.IllegalArgumentException
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+ public Virtualizer(int priority, int audioSession)
+ throws IllegalStateException, IllegalArgumentException,
+ UnsupportedOperationException, RuntimeException {
+ super(EFFECT_TYPE_VIRTUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
+
+ if (audioSession == 0) {
+ Log.w(TAG, "WARNING: attaching a Virtualizer to global output mix is deprecated!");
+ }
+
+ int[] value = new int[1];
+ checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
+ mStrengthSupported = (value[0] != 0);
+ }
+
+ /**
+ * Indicates whether setting strength is supported. If this method returns false, only one
+ * strength is supported and the setStrength() method always rounds to that value.
+ * @return true is strength parameter is supported, false otherwise
+ */
+ public boolean getStrengthSupported() {
+ return mStrengthSupported;
+ }
+
+ /**
+ * Sets the strength of the virtualizer effect. If the implementation does not support per mille
+ * accuracy for setting the strength, it is allowed to round the given strength to the nearest
+ * supported value. You can use the {@link #getRoundedStrength()} method to query the
+ * (possibly rounded) value that was actually set.
+ * @param strength strength of the effect. The valid range for strength strength is [0, 1000],
+ * where 0 per mille designates the mildest effect and 1000 per mille designates the strongest.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setStrength(short strength)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_STRENGTH, strength));
+ }
+
+ /**
+ * Gets the current strength of the effect.
+ * @return the strength of the effect. The valid range for strength is [0, 1000], where 0 per
+ * mille designates the mildest effect and 1000 per mille the strongest
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public short getRoundedStrength()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ short[] value = new short[1];
+ checkStatus(getParameter(PARAM_STRENGTH, value));
+ return value[0];
+ }
+
+ /**
+ * Checks if a configuration is supported, and query the virtual speaker angles.
+ * @param inputChannelMask
+ * @param deviceType
+ * @param angles if non-null: array in which the angles will be written. If null, no angles
+ * are returned
+ * @return true if the combination of channel mask and output device type is supported, false
+ * otherwise
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ private boolean getAnglesInt(int inputChannelMask, int deviceType, int[] angles)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ // parameter check
+ if (inputChannelMask == AudioFormat.CHANNEL_INVALID) {
+ throw (new IllegalArgumentException(
+ "Virtualizer: illegal CHANNEL_INVALID channel mask"));
+ }
+ int channelMask = inputChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT ?
+ AudioFormat.CHANNEL_OUT_STEREO : inputChannelMask;
+ int nbChannels = AudioFormat.channelCountFromOutChannelMask(channelMask);
+ if ((angles != null) && (angles.length < (nbChannels * 3))) {
+ Log.e(TAG, "Size of array for angles cannot accomodate number of channels in mask ("
+ + nbChannels + ")");
+ throw (new IllegalArgumentException(
+ "Virtualizer: array for channel / angle pairs is too small: is " + angles.length
+ + ", should be " + (nbChannels * 3)));
+ }
+
+ ByteBuffer paramsConverter = ByteBuffer.allocate(3 /* param + mask + device*/ * 4);
+ paramsConverter.order(ByteOrder.nativeOrder());
+ paramsConverter.putInt(PARAM_VIRTUAL_SPEAKER_ANGLES);
+ // convert channel mask to internal native representation
+ paramsConverter.putInt(AudioFormat.convertChannelOutMaskToNativeMask(channelMask));
+ // convert Java device type to internal representation
+ paramsConverter.putInt(AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType));
+ // allocate an array to store the results
+ byte[] result = new byte[nbChannels * 4/*int to byte*/ * 3/*for mask, azimuth, elevation*/];
+
+ // call into the effect framework
+ int status = getParameter(paramsConverter.array(), result);
+ if (DEBUG) {
+ Log.v(TAG, "getAngles(0x" + Integer.toHexString(inputChannelMask) + ", 0x"
+ + Integer.toHexString(deviceType) + ") returns " + status);
+ }
+
+ if (status >= 0) {
+ if (angles != null) {
+ // convert and copy the results
+ ByteBuffer resultConverter = ByteBuffer.wrap(result);
+ resultConverter.order(ByteOrder.nativeOrder());
+ for (int i = 0 ; i < nbChannels ; i++) {
+ // write the channel mask
+ angles[3 * i] = AudioFormat.convertNativeChannelMaskToOutMask(
+ resultConverter.getInt((i * 4 * 3)));
+ // write the azimuth
+ angles[3 * i + 1] = resultConverter.getInt(i * 4 * 3 + 4);
+ // write the elevation
+ angles[3 * i + 2] = resultConverter.getInt(i * 4 * 3 + 8);
+ if (DEBUG) {
+ Log.v(TAG, "channel 0x" + Integer.toHexString(angles[3*i]).toUpperCase()
+ + " at az=" + angles[3*i+1] + "deg"
+ + " elev=" + angles[3*i+2] + "deg");
+ }
+ }
+ }
+ return true;
+ } else if (status == AudioEffect.ERROR_BAD_VALUE) {
+ // a BAD_VALUE return from getParameter indicates the configuration is not supported
+ // don't throw an exception, just return false
+ return false;
+ } else {
+ // something wrong may have happened
+ checkStatus(status);
+ }
+ // unexpected virtualizer behavior
+ Log.e(TAG, "unexpected status code " + status
+ + " after getParameter(PARAM_VIRTUAL_SPEAKER_ANGLES)");
+ return false;
+ }
+
+ /**
+ * A virtualization mode indicating virtualization processing is not active.
+ * See {@link #getVirtualizationMode()} as one of the possible return value.
+ */
+ public static final int VIRTUALIZATION_MODE_OFF = 0;
+
+ /**
+ * A virtualization mode used to indicate the virtualizer effect must stop forcing the
+ * processing to a particular mode in {@link #forceVirtualizationMode(int)}.
+ */
+ public static final int VIRTUALIZATION_MODE_AUTO = 1;
+ /**
+ * A virtualization mode typically used over headphones.
+ * Binaural virtualization describes an audio processing configuration for virtualization
+ * where the left and right channels are respectively reaching the left and right ear of the
+ * user, without also feeding the opposite ear (as is the case when listening over speakers).
+ * <p>Such a mode is therefore meant to be used when audio is playing over stereo wired
+ * headphones or headsets, but also stereo headphones through a wireless A2DP Bluetooth link.
+ * <p>See {@link #canVirtualize(int, int)} to verify this mode is supported by this Virtualizer.
+ */
+ public final static int VIRTUALIZATION_MODE_BINAURAL = 2;
+
+ /**
+ * A virtualization mode typically used over speakers.
+ * Transaural virtualization describes an audio processing configuration that differs from
+ * binaural (as described in {@link #VIRTUALIZATION_MODE_BINAURAL} in that cross-talk is
+ * present, i.e. audio played from the left channel also reaches the right ear of the user,
+ * and vice-versa.
+ * <p>When supported, such a mode is therefore meant to be used when audio is playing over the
+ * built-in stereo speakers of a device, if they are featured.
+ * <p>See {@link #canVirtualize(int, int)} to verify this mode is supported by this Virtualizer.
+ */
+ public final static int VIRTUALIZATION_MODE_TRANSAURAL = 3;
+
+ /** @hide */
+ @IntDef( {
+ VIRTUALIZATION_MODE_BINAURAL,
+ VIRTUALIZATION_MODE_TRANSAURAL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VirtualizationMode {}
+
+ /** @hide */
+ @IntDef( {
+ VIRTUALIZATION_MODE_AUTO,
+ VIRTUALIZATION_MODE_BINAURAL,
+ VIRTUALIZATION_MODE_TRANSAURAL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ForceVirtualizationMode {}
+
+ private static int getDeviceForModeQuery(@VirtualizationMode int virtualizationMode)
+ throws IllegalArgumentException {
+ switch (virtualizationMode) {
+ case VIRTUALIZATION_MODE_BINAURAL:
+ return AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
+ case VIRTUALIZATION_MODE_TRANSAURAL:
+ return AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+ default:
+ throw (new IllegalArgumentException(
+ "Virtualizer: illegal virtualization mode " + virtualizationMode));
+ }
+ }
+
+ private static int getDeviceForModeForce(@ForceVirtualizationMode int virtualizationMode)
+ throws IllegalArgumentException {
+ if (virtualizationMode == VIRTUALIZATION_MODE_AUTO) {
+ return AudioDeviceInfo.TYPE_UNKNOWN;
+ } else {
+ return getDeviceForModeQuery(virtualizationMode);
+ }
+ }
+
+ private static int deviceToMode(int deviceType) {
+ switch (deviceType) {
+ case AudioDeviceInfo.TYPE_WIRED_HEADSET:
+ case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
+ case AudioDeviceInfo.TYPE_BLUETOOTH_SCO:
+ case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
+ case AudioDeviceInfo.TYPE_USB_HEADSET:
+ return VIRTUALIZATION_MODE_BINAURAL;
+ case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
+ case AudioDeviceInfo.TYPE_LINE_ANALOG:
+ case AudioDeviceInfo.TYPE_LINE_DIGITAL:
+ case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
+ case AudioDeviceInfo.TYPE_HDMI:
+ case AudioDeviceInfo.TYPE_HDMI_ARC:
+ case AudioDeviceInfo.TYPE_USB_DEVICE:
+ case AudioDeviceInfo.TYPE_USB_ACCESSORY:
+ case AudioDeviceInfo.TYPE_DOCK:
+ case AudioDeviceInfo.TYPE_FM:
+ case AudioDeviceInfo.TYPE_AUX_LINE:
+ return VIRTUALIZATION_MODE_TRANSAURAL;
+ case AudioDeviceInfo.TYPE_UNKNOWN:
+ default:
+ return VIRTUALIZATION_MODE_OFF;
+ }
+ }
+
+ /**
+ * Checks if the combination of a channel mask and virtualization mode is supported by this
+ * virtualizer.
+ * Some virtualizer implementations may only support binaural processing (i.e. only support
+ * headphone output, see {@link #VIRTUALIZATION_MODE_BINAURAL}), some may support transaural
+ * processing (i.e. for speaker output, see {@link #VIRTUALIZATION_MODE_TRANSAURAL}) for the
+ * built-in speakers. Use this method to query the virtualizer implementation capabilities.
+ * @param inputChannelMask the channel mask of the content to virtualize.
+ * @param virtualizationMode the mode for which virtualization processing is to be performed,
+ * one of {@link #VIRTUALIZATION_MODE_BINAURAL}, {@link #VIRTUALIZATION_MODE_TRANSAURAL}.
+ * @return true if the combination of channel mask and virtualization mode is supported, false
+ * otherwise.
+ * <br>An indication that a certain channel mask is not supported doesn't necessarily mean
+ * you cannot play content with that channel mask, it more likely implies the content will
+ * be downmixed before being virtualized. For instance a virtualizer that only supports a
+ * mask such as {@link AudioFormat#CHANNEL_OUT_STEREO}
+ * will still be able to process content with a mask of
+ * {@link AudioFormat#CHANNEL_OUT_5POINT1}, but will downmix the content to stereo first, and
+ * then will virtualize, as opposed to virtualizing each channel individually.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public boolean canVirtualize(int inputChannelMask, @VirtualizationMode int virtualizationMode)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ return getAnglesInt(inputChannelMask, getDeviceForModeQuery(virtualizationMode), null);
+ }
+
+ /**
+ * Queries the virtual speaker angles (azimuth and elevation) for a combination of a channel
+ * mask and virtualization mode.
+ * If the virtualization configuration (mask and mode) is supported (see
+ * {@link #canVirtualize(int, int)}, the array angles will contain upon return the
+ * definition of each virtual speaker and its azimuth and elevation angles relative to the
+ * listener.
+ * <br>Note that in some virtualizer implementations, the angles may be strength-dependent.
+ * @param inputChannelMask the channel mask of the content to virtualize.
+ * @param virtualizationMode the mode for which virtualization processing is to be performed,
+ * one of {@link #VIRTUALIZATION_MODE_BINAURAL}, {@link #VIRTUALIZATION_MODE_TRANSAURAL}.
+ * @param angles a non-null array whose length is 3 times the number of channels in the channel
+ * mask.
+ * If the method indicates the configuration is supported, the array will contain upon return
+ * triplets of values: for each channel <code>i</code> among the channels of the mask:
+ * <ul>
+ * <li>the element at index <code>3*i</code> in the array contains the speaker
+ * identification (e.g. {@link AudioFormat#CHANNEL_OUT_FRONT_LEFT}),</li>
+ * <li>the element at index <code>3*i+1</code> contains its corresponding azimuth angle
+ * expressed in degrees, where 0 is the direction the listener faces, 180 is behind
+ * the listener, and -90 is to her/his left,</li>
+ * <li>the element at index <code>3*i+2</code> contains its corresponding elevation angle
+ * where +90 is directly above the listener, 0 is the horizontal plane, and -90 is
+ * directly below the listener.</li>
+ * @return true if the combination of channel mask and virtualization mode is supported, false
+ * otherwise.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public boolean getSpeakerAngles(int inputChannelMask,
+ @VirtualizationMode int virtualizationMode, int[] angles)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ if (angles == null) {
+ throw (new IllegalArgumentException(
+ "Virtualizer: illegal null channel / angle array"));
+ }
+
+ return getAnglesInt(inputChannelMask, getDeviceForModeQuery(virtualizationMode), angles);
+ }
+
+ /**
+ * Forces the virtualizer effect to use the given processing mode.
+ * The effect must be enabled for the forced mode to be applied.
+ * @param virtualizationMode one of {@link #VIRTUALIZATION_MODE_BINAURAL},
+ * {@link #VIRTUALIZATION_MODE_TRANSAURAL} to force a particular processing mode, or
+ * {@value #VIRTUALIZATION_MODE_AUTO} to stop forcing a mode.
+ * @return true if the processing mode is supported, and it is successfully set, or
+ * forcing was successfully disabled, false otherwise.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public boolean forceVirtualizationMode(@ForceVirtualizationMode int virtualizationMode)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ // convert Java device type to internal representation
+ int deviceType = getDeviceForModeForce(virtualizationMode);
+ int internalDevice = AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType);
+
+ int status = setParameter(PARAM_FORCE_VIRTUALIZATION_MODE, internalDevice);
+
+ if (status >= 0) {
+ return true;
+ } else if (status == AudioEffect.ERROR_BAD_VALUE) {
+ // a BAD_VALUE return from setParameter indicates the mode can't be forced
+ // don't throw an exception, just return false
+ return false;
+ } else {
+ // something wrong may have happened
+ checkStatus(status);
+ }
+ // unexpected virtualizer behavior
+ Log.e(TAG, "unexpected status code " + status
+ + " after setParameter(PARAM_FORCE_VIRTUALIZATION_MODE)");
+ return false;
+ }
+
+ /**
+ * Return the virtualization mode being used, if any.
+ * @return the virtualization mode being used.
+ * If virtualization is not active, the virtualization mode will be
+ * {@link #VIRTUALIZATION_MODE_OFF}. Otherwise the value will be
+ * {@link #VIRTUALIZATION_MODE_BINAURAL} or {@link #VIRTUALIZATION_MODE_TRANSAURAL}.
+ * Virtualization may not be active either because the effect is not enabled or
+ * because the current output device is not compatible with this virtualization
+ * implementation.
+ * @throws IllegalStateException
+ * @throws UnsupportedOperationException
+ */
+ public int getVirtualizationMode()
+ throws IllegalStateException, UnsupportedOperationException {
+ int[] value = new int[1];
+ int status = getParameter(PARAM_VIRTUALIZATION_MODE, value);
+ if (status >= 0) {
+ return deviceToMode(AudioDeviceInfo.convertInternalDeviceToDeviceType(value[0]));
+ } else if (status == AudioEffect.ERROR_BAD_VALUE) {
+ return VIRTUALIZATION_MODE_OFF;
+ } else {
+ // something wrong may have happened
+ checkStatus(status);
+ }
+ // unexpected virtualizer behavior
+ Log.e(TAG, "unexpected status code " + status
+ + " after getParameter(PARAM_VIRTUALIZATION_MODE)");
+ return VIRTUALIZATION_MODE_OFF;
+ }
+
+ /**
+ * The OnParameterChangeListener interface defines a method called by the Virtualizer when a
+ * parameter value has changed.
+ */
+ public interface OnParameterChangeListener {
+ /**
+ * Method called when a parameter value has changed. The method is called only if the
+ * parameter was changed by another application having the control of the same
+ * Virtualizer engine.
+ * @param effect the Virtualizer on which the interface is registered.
+ * @param status status of the set parameter operation.
+ * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ...
+ * @param value the new parameter value.
+ */
+ void onParameterChange(Virtualizer effect, int status, int param, short value);
+ }
+
+ /**
+ * Listener used internally to receive unformatted parameter change events from AudioEffect
+ * super class.
+ */
+ private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
+ private BaseParameterListener() {
+
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ OnParameterChangeListener l = null;
+
+ synchronized (mParamListenerLock) {
+ if (mParamListener != null) {
+ l = mParamListener;
+ }
+ }
+ if (l != null) {
+ int p = -1;
+ short v = -1;
+
+ if (param.length == 4) {
+ p = byteArrayToInt(param, 0);
+ }
+ if (value.length == 2) {
+ v = byteArrayToShort(value, 0);
+ }
+ if (p != -1 && v != -1) {
+ l.onParameterChange(Virtualizer.this, status, p, v);
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers an OnParameterChangeListener interface.
+ * @param listener OnParameterChangeListener interface registered
+ */
+ public void setParameterListener(OnParameterChangeListener listener) {
+ synchronized (mParamListenerLock) {
+ if (mParamListener == null) {
+ mParamListener = listener;
+ mBaseParamListener = new BaseParameterListener();
+ super.setParameterListener(mBaseParamListener);
+ }
+ }
+ }
+
+ /**
+ * The Settings class regroups all virtualizer parameters. It is used in
+ * conjuntion with getProperties() and setProperties() methods to backup and restore
+ * all parameters in a single call.
+ */
+ public static class Settings {
+ public short strength;
+
+ public Settings() {
+ }
+
+ /**
+ * Settings class constructor from a key=value; pairs formatted string. The string is
+ * typically returned by Settings.toString() method.
+ * @throws IllegalArgumentException if the string is not correctly formatted.
+ */
+ public Settings(String settings) {
+ StringTokenizer st = new StringTokenizer(settings, "=;");
+ int tokens = st.countTokens();
+ if (st.countTokens() != 3) {
+ throw new IllegalArgumentException("settings: " + settings);
+ }
+ String key = st.nextToken();
+ if (!key.equals("Virtualizer")) {
+ throw new IllegalArgumentException(
+ "invalid settings for Virtualizer: " + key);
+ }
+ try {
+ key = st.nextToken();
+ if (!key.equals("strength")) {
+ throw new IllegalArgumentException("invalid key name: " + key);
+ }
+ strength = Short.parseShort(st.nextToken());
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("invalid value for key: " + key);
+ }
+ }
+
+ @Override
+ public String toString() {
+ String str = new String (
+ "Virtualizer"+
+ ";strength="+Short.toString(strength)
+ );
+ return str;
+ }
+ };
+
+
+ /**
+ * Gets the virtualizer properties. This method is useful when a snapshot of current
+ * virtualizer settings must be saved by the application.
+ * @return a Virtualizer.Settings object containing all current parameters values
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public Virtualizer.Settings getProperties()
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ Settings settings = new Settings();
+ short[] value = new short[1];
+ checkStatus(getParameter(PARAM_STRENGTH, value));
+ settings.strength = value[0];
+ return settings;
+ }
+
+ /**
+ * Sets the virtualizer properties. This method is useful when virtualizer settings have to
+ * be applied from a previous backup.
+ * @param settings a Virtualizer.Settings object containing the properties to apply
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public void setProperties(Virtualizer.Settings settings)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ checkStatus(setParameter(PARAM_STRENGTH, settings.strength));
+ }
+}
diff --git a/android/media/audiofx/Visualizer.java b/android/media/audiofx/Visualizer.java
new file mode 100644
index 00000000..0fe7246e
--- /dev/null
+++ b/android/media/audiofx/Visualizer.java
@@ -0,0 +1,772 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.app.ActivityThread;
+import android.util.Log;
+import java.lang.ref.WeakReference;
+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 android.media.MediaPlayer} or
+ * {@link android.media.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. 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.
+ * <p>Creating a Visualizer on the output mix (audio session 0) requires permission
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
+ * <p>The Visualizer class can also be used to perform measurements on the audio being played back.
+ * The measurements to perform are defined by setting a mask of the requested measurement modes with
+ * {@link #setMeasurementMode(int)}. Supported values are {@link #MEASUREMENT_MODE_NONE} to cancel
+ * any measurement, and {@link #MEASUREMENT_MODE_PEAK_RMS} for peak and RMS monitoring.
+ * Measurements can be retrieved through {@link #getMeasurementPeakRms(MeasurementPeakRms)}.
+ */
+
+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 system/media/audio_effects/include/audio_effects/effect_visualizer.h
+ /**
+ * Defines a capture mode where amplification is applied based on the content of the captured
+ * data. This is the default Visualizer mode, and is suitable for music visualization.
+ */
+ public static final int SCALING_MODE_NORMALIZED = 0;
+ /**
+ * Defines a capture mode where the playback volume will affect (scale) the range of the
+ * captured data. A low playback volume will lead to low sample and fft values, and vice-versa.
+ */
+ public static final int SCALING_MODE_AS_PLAYED = 1;
+
+ /**
+ * Defines a measurement mode in which no measurements are performed.
+ */
+ public static final int MEASUREMENT_MODE_NONE = 0;
+
+ /**
+ * Defines a measurement mode which computes the peak and RMS value in mB, where 0mB is the
+ * maximum sample value, and -9600mB is the minimum value.
+ * Values for peak and RMS can be retrieved with
+ * {@link #getMeasurementPeakRms(MeasurementPeakRms)}.
+ */
+ public static final int MEASUREMENT_MODE_PEAK_RMS = 1 << 0;
+
+ // to keep in sync with frameworks/base/media/jni/audioeffect/android_media_Visualizer.cpp
+ private static final int NATIVE_EVENT_PCM_CAPTURE = 0;
+ private static final int NATIVE_EVENT_FFT_CAPTURE = 1;
+ private static final int NATIVE_EVENT_SERVER_DIED = 2;
+
+ // Error codes:
+ /**
+ * Successful operation.
+ */
+ public static final int SUCCESS = 0;
+ /**
+ * Unspecified error.
+ */
+ public static final int ERROR = -1;
+ /**
+ * Internal operation 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
+ */
+ private int mState = STATE_UNINITIALIZED;
+ /**
+ * Lock to synchronize access to mState
+ */
+ private final Object mStateLock = new Object();
+ /**
+ * System wide unique Identifier of the visualizer engine used by this Visualizer instance
+ */
+ private int mId;
+
+ /**
+ * Lock to protect listeners updates against event notifications
+ */
+ private final Object mListenerLock = new Object();
+ /**
+ * Handler for events coming from the native code
+ */
+ private NativeEventHandler mNativeEventHandler = null;
+ /**
+ * PCM and FFT capture listener registered by client
+ */
+ private OnDataCaptureListener mCaptureListener = null;
+ /**
+ * Server Died listener registered by client
+ */
+ private OnServerDiedListener mServerDiedListener = null;
+
+ // accessed by native methods
+ private long mNativeVisualizer;
+ private long 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,
+ ActivityThread.currentOpPackageName());
+ 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 (mState == STATE_UNINITIALIZED) {
+ throw(new IllegalStateException("setEnabled() called in wrong state: "+mState));
+ }
+ int status = SUCCESS;
+ if ((enabled && (mState == STATE_INITIALIZED)) ||
+ (!enabled && (mState == STATE_ENABLED))) {
+ 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();
+ }
+ }
+
+ /**
+ * Set the type of scaling applied on the captured visualization data.
+ * @param mode see {@link #SCALING_MODE_NORMALIZED}
+ * and {@link #SCALING_MODE_AS_PLAYED}
+ * @return {@link #SUCCESS} in case of success,
+ * {@link #ERROR_BAD_VALUE} in case of failure.
+ * @throws IllegalStateException
+ */
+ public int setScalingMode(int mode)
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState == STATE_UNINITIALIZED) {
+ throw(new IllegalStateException("setScalingMode() called in wrong state: "
+ + mState));
+ }
+ return native_setScalingMode(mode);
+ }
+ }
+
+ /**
+ * Returns the current scaling mode on the captured visualization data.
+ * @return the scaling mode, see {@link #SCALING_MODE_NORMALIZED}
+ * and {@link #SCALING_MODE_AS_PLAYED}.
+ * @throws IllegalStateException
+ */
+ public int getScalingMode()
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState == STATE_UNINITIALIZED) {
+ throw(new IllegalStateException("getScalingMode() called in wrong state: "
+ + mState));
+ }
+ return native_getScalingMode();
+ }
+ }
+
+ /**
+ * Sets the combination of measurement modes to be performed by this audio effect.
+ * @param mode a mask of the measurements to perform. The valid values are
+ * {@link #MEASUREMENT_MODE_NONE} (to cancel any measurement)
+ * or {@link #MEASUREMENT_MODE_PEAK_RMS}.
+ * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE} in case of failure.
+ * @throws IllegalStateException
+ */
+ public int setMeasurementMode(int mode)
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState == STATE_UNINITIALIZED) {
+ throw(new IllegalStateException("setMeasurementMode() called in wrong state: "
+ + mState));
+ }
+ return native_setMeasurementMode(mode);
+ }
+ }
+
+ /**
+ * Returns the current measurement modes performed by this audio effect
+ * @return the mask of the measurements,
+ * {@link #MEASUREMENT_MODE_NONE} (when no measurements are performed)
+ * or {@link #MEASUREMENT_MODE_PEAK_RMS}.
+ * @throws IllegalStateException
+ */
+ public int getMeasurementMode()
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState == STATE_UNINITIALIZED) {
+ throw(new IllegalStateException("getMeasurementMode() called in wrong state: "
+ + mState));
+ }
+ return native_getMeasurementMode();
+ }
+ }
+
+ /**
+ * 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.
+ * <p>This method must be called when the Visualizer is enabled.
+ * <p>The capture is an 8-bit magnitude FFT, the frequency range covered being 0 (DC) to half of
+ * the sampling rate returned by {@link #getSamplingRate()}. The capture returns the real and
+ * imaginary parts of a number of frequency points equal to half of the capture size plus one.
+ * <p>Note: only the real part is returned for the first point (DC) and the last point
+ * (sampling frequency / 2).
+ * <p>The layout in the returned byte array is as follows:
+ * <ul>
+ * <li> n is the capture size returned by getCaptureSize()</li>
+ * <li> Rfk, Ifk are respectively the real and imaginary parts of the kth frequency
+ * component</li>
+ * <li> If Fs is the sampling frequency retuned by getSamplingRate() the kth frequency is:
+ * (k*Fs)/(n/2) </li>
+ * </ul>
+ * <table border="0" cellspacing="0" cellpadding="0">
+ * <tr><td>Index </p></td>
+ * <td>0 </p></td>
+ * <td>1 </p></td>
+ * <td>2 </p></td>
+ * <td>3 </p></td>
+ * <td>4 </p></td>
+ * <td>5 </p></td>
+ * <td>... </p></td>
+ * <td>n - 2 </p></td>
+ * <td>n - 1 </p></td></tr>
+ * <tr><td>Data </p></td>
+ * <td>Rf0 </p></td>
+ * <td>Rf(n/2) </p></td>
+ * <td>Rf1 </p></td>
+ * <td>If1 </p></td>
+ * <td>Rf2 </p></td>
+ * <td>If2 </p></td>
+ * <td>... </p></td>
+ * <td>Rf(n-1)/2 </p></td>
+ * <td>If(n-1)/2 </p></td></tr>
+ * </table>
+ * @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);
+ }
+ }
+
+ /**
+ * A class to store peak and RMS values.
+ * Peak and RMS are expressed in mB, as described in the
+ * {@link Visualizer#MEASUREMENT_MODE_PEAK_RMS} measurement mode.
+ */
+ public static final class MeasurementPeakRms {
+ /**
+ * The peak value in mB.
+ */
+ public int mPeak;
+ /**
+ * The RMS value in mB.
+ */
+ public int mRms;
+ }
+
+ /**
+ * Retrieves the latest peak and RMS measurement.
+ * Sets the peak and RMS fields of the supplied {@link Visualizer.MeasurementPeakRms} to the
+ * latest measured values.
+ * @param measurement a non-null {@link Visualizer.MeasurementPeakRms} instance to store
+ * the measurement values.
+ * @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.
+ */
+ public int getMeasurementPeakRms(MeasurementPeakRms measurement) {
+ if (measurement == null) {
+ Log.e(TAG, "Cannot store measurements in a null object");
+ return ERROR_BAD_VALUE;
+ }
+ synchronized (mStateLock) {
+ if (mState != STATE_ENABLED) {
+ throw (new IllegalStateException("getMeasurementPeakRms() called in wrong state: "
+ + mState));
+ }
+ return native_getPeakRms(measurement);
+ }
+ }
+
+ //---------------------------------------------------------
+ // 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.
+ * <p>Data in the waveform buffer is valid only within the scope of the callback.
+ * Applications which needs access to the waveform data after returning from the callback
+ * should make a copy of the data instead of holding a reference.
+ * @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.
+ * <p>Data in the fft buffer is valid only within the scope of the callback.
+ * Applications which needs access to the fft data after returning from the callback
+ * should make a copy of the data instead of holding a reference.
+ * @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;
+ }
+
+ /**
+ * @hide
+ *
+ * The OnServerDiedListener interface defines a method called by the Visualizer to indicate that
+ * the connection to the native media server has been broken and that the Visualizer object will
+ * need to be released and re-created.
+ * The client application can implement this interface and register the listener with the
+ * {@link #setServerDiedListener(OnServerDiedListener)} method.
+ */
+ public interface OnServerDiedListener {
+ /**
+ * @hide
+ *
+ * Method called when the native media server has died.
+ * <p>If the native media server encounters a fatal error and needs to restart, the binder
+ * connection from the {@link #Visualizer} to the media server will be broken. Data capture
+ * callbacks will stop happening, and client initiated calls to the {@link #Visualizer}
+ * instance will fail with the error code {@link #DEAD_OBJECT}. To restore functionality,
+ * clients should {@link #release()} their old visualizer and create a new instance.
+ */
+ void onServerDied();
+ }
+
+ /**
+ * @hide
+ *
+ * Registers an OnServerDiedListener interface.
+ * <p>Call this method with a null listener to stop receiving server death notifications.
+ * @return {@link #SUCCESS} in case of success,
+ */
+ public int setServerDiedListener(OnServerDiedListener listener) {
+ synchronized (mListenerLock) {
+ mServerDiedListener = listener;
+ }
+ return SUCCESS;
+ }
+
+ /**
+ * 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;
+ }
+
+ private void handleCaptureMessage(Message msg) {
+ 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 in handleCaptureMessge: "+msg.what);
+ break;
+ }
+ }
+ }
+
+ private void handleServerDiedMessage(Message msg) {
+ OnServerDiedListener l = null;
+ synchronized (mListenerLock) {
+ l = mVisualizer.mServerDiedListener;
+ }
+
+ if (l != null)
+ l.onServerDied();
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (mVisualizer == null) {
+ return;
+ }
+
+ switch(msg.what) {
+ case NATIVE_EVENT_PCM_CAPTURE:
+ case NATIVE_EVENT_FFT_CAPTURE:
+ handleCaptureMessage(msg);
+ break;
+ case NATIVE_EVENT_SERVER_DIED:
+ handleServerDiedMessage(msg);
+ 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,
+ String opPackageName);
+
+ 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_setScalingMode(int mode);
+
+ private native final int native_getScalingMode();
+
+ private native final int native_setMeasurementMode(int mode);
+
+ private native final int native_getMeasurementMode();
+
+ 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_getPeakRms(MeasurementPeakRms measurement);
+
+ 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);
+ }
+
+ }
+}
+