Home | History | Annotate | Download | only in audiofx
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.media.audiofx;
     18 
     19 import android.annotation.SdkConstant;
     20 import android.annotation.SdkConstant.SdkConstantType;
     21 import android.os.Handler;
     22 import android.os.Looper;
     23 import android.os.Message;
     24 import android.util.Log;
     25 import java.io.IOException;
     26 import java.lang.ref.WeakReference;
     27 import java.nio.ByteOrder;
     28 import java.nio.ByteBuffer;
     29 import java.util.UUID;
     30 
     31 /**
     32  * AudioEffect is the base class for controlling audio effects provided by the android audio
     33  * framework.
     34  * <p>Applications should not use the AudioEffect class directly but one of its derived classes to
     35  * control specific effects:
     36  * <ul>
     37  *   <li> {@link android.media.audiofx.Equalizer}</li>
     38  *   <li> {@link android.media.audiofx.Virtualizer}</li>
     39  *   <li> {@link android.media.audiofx.BassBoost}</li>
     40  *   <li> {@link android.media.audiofx.PresetReverb}</li>
     41  *   <li> {@link android.media.audiofx.EnvironmentalReverb}</li>
     42  * </ul>
     43  * <p>If the audio effect is to be applied to a specific AudioTrack or MediaPlayer instance,
     44  * the application must specify the audio session ID of that instance when creating the AudioEffect.
     45  * (see {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions).
     46  * To apply an effect to the global audio output mix, session 0 must be specified when creating the
     47  * AudioEffect.
     48  * <p>Creating an effect on the output mix (audio session 0) requires permission
     49  * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
     50  * <p>Creating an AudioEffect object will create the corresponding effect engine in the audio
     51  * framework if no instance of the same effect type exists in the specified audio session.
     52  * If one exists, this instance will be used.
     53  * <p>The application creating the AudioEffect object (or a derived class) will either receive
     54  * control of the effect engine or not depending on the priority parameter. If priority is higher
     55  * than the priority used by the current effect engine owner, the control will be transfered to the
     56  * new object. Otherwise control will remain with the previous object. In this case, the new
     57  * application will be notified of changes in effect engine state or control ownership by the
     58  * appropiate listener.
     59  */
     60 
     61 public class AudioEffect {
     62     static {
     63         System.loadLibrary("audioeffect_jni");
     64         native_init();
     65     }
     66 
     67     private final static String TAG = "AudioEffect-JAVA";
     68 
     69     /**
     70      * The following UUIDs define effect types corresponding to standard audio
     71      * effects whose implementation and interface conform to the OpenSL ES
     72      * specification. The definitions match the corresponding interface IDs in
     73      * OpenSLES_IID.h
     74      */
     75 
     76     /**
     77      * UUID for environmental reverb effect
     78      * @hide
     79      */
     80     public static final UUID EFFECT_TYPE_ENV_REVERB = UUID
     81             .fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e");
     82     /**
     83      * UUID for preset reverb effect
     84      * @hide
     85      */
     86     public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID
     87             .fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b");
     88     /**
     89      * UUID for equalizer effect
     90      * @hide
     91      */
     92     public static final UUID EFFECT_TYPE_EQUALIZER = UUID
     93             .fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b");
     94     /**
     95      * UUID for bass boost effect
     96      * @hide
     97      */
     98     public static final UUID EFFECT_TYPE_BASS_BOOST = UUID
     99             .fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b");
    100     /**
    101      * UUID for virtualizer effect
    102      * @hide
    103      */
    104     public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID
    105             .fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b");
    106 
    107     /**
    108      * Null effect UUID. Used when the UUID for effect type of
    109      * @hide
    110      */
    111     public static final UUID EFFECT_TYPE_NULL = UUID
    112             .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210");
    113 
    114     /**
    115      * State of an AudioEffect object that was not successfully initialized upon
    116      * creation
    117      * @hide
    118      */
    119     public static final int STATE_UNINITIALIZED = 0;
    120     /**
    121      * State of an AudioEffect object that is ready to be used.
    122      * @hide
    123      */
    124     public static final int STATE_INITIALIZED = 1;
    125 
    126     // to keep in sync with
    127     // frameworks/base/include/media/AudioEffect.h
    128     /**
    129      * Event id for engine control ownership change notification.
    130      * @hide
    131      */
    132     public static final int NATIVE_EVENT_CONTROL_STATUS = 0;
    133     /**
    134      * Event id for engine state change notification.
    135      * @hide
    136      */
    137     public static final int NATIVE_EVENT_ENABLED_STATUS = 1;
    138     /**
    139      * Event id for engine parameter change notification.
    140      * @hide
    141      */
    142     public static final int NATIVE_EVENT_PARAMETER_CHANGED = 2;
    143 
    144     /**
    145      * Successful operation.
    146      */
    147     public static final int SUCCESS = 0;
    148     /**
    149      * Unspecified error.
    150      */
    151     public static final int ERROR = -1;
    152     /**
    153      * Internal opreation status. Not returned by any method.
    154      */
    155     public static final int ALREADY_EXISTS = -2;
    156     /**
    157      * Operation failed due to bad object initialization.
    158      */
    159     public static final int ERROR_NO_INIT = -3;
    160     /**
    161      * Operation failed due to bad parameter value.
    162      */
    163     public static final int ERROR_BAD_VALUE = -4;
    164     /**
    165      * Operation failed because it was requested in wrong state.
    166      */
    167     public static final int ERROR_INVALID_OPERATION = -5;
    168     /**
    169      * Operation failed due to lack of memory.
    170      */
    171     public static final int ERROR_NO_MEMORY = -6;
    172     /**
    173      * Operation failed due to dead remote object.
    174      */
    175     public static final int ERROR_DEAD_OBJECT = -7;
    176 
    177     /**
    178      * The effect descriptor contains information on a particular effect implemented in the
    179      * audio framework:<br>
    180      * <ul>
    181      *  <li>type: UUID corresponding to the OpenSL ES interface implemented by this effect</li>
    182      *  <li>uuid: UUID for this particular implementation</li>
    183      *  <li>connectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li>
    184      *  <li>name: human readable effect name</li>
    185      *  <li>implementor: human readable effect implementor name</li>
    186      * </ul>
    187      * The method {@link #queryEffects()} returns an array of Descriptors to facilitate effects
    188      * enumeration.
    189      */
    190     public static class Descriptor {
    191 
    192         public Descriptor() {
    193         }
    194 
    195         public Descriptor(String type, String uuid, String connectMode,
    196                 String name, String implementor) {
    197             this.type = UUID.fromString(type);
    198             this.uuid = UUID.fromString(uuid);
    199             this.connectMode = connectMode;
    200             this.name = name;
    201             this.implementor = implementor;
    202         }
    203 
    204         /**
    205          *  Indicates the generic type of the effect (Equalizer, Bass boost ...). The UUID
    206          *  corresponds to the OpenSL ES Interface ID for this type of effect.
    207          */
    208         public UUID type;
    209         /**
    210          *  Indicates the particular implementation of the effect in that type. Several effects
    211          *  can have the same type but this uuid is unique to a given implementation.
    212          */
    213         public UUID uuid;
    214         /**
    215          *  Indicates if the effect is of insert category {@link #EFFECT_INSERT} or auxiliary
    216          *  category {@link #EFFECT_AUXILIARY}. Insert effects (Typically an Equalizer) are applied
    217          *  to the entire audio source and usually not shared by several sources. Auxiliary effects
    218          *  (typically a reverberator) are applied to part of the signal (wet) and the effect output
    219          *  is added to the original signal (dry).
    220          */
    221         public String connectMode;
    222         /**
    223          * Human readable effect name
    224          */
    225         public String name;
    226         /**
    227          * Human readable effect implementor name
    228          */
    229         public String implementor;
    230     };
    231 
    232     /**
    233      * Effect connection mode is insert. Specifying an audio session ID when creating the effect
    234      * will insert this effect after all players in the same audio session.
    235      */
    236     public static final String EFFECT_INSERT = "Insert";
    237     /**
    238      * Effect connection mode is auxiliary.
    239      * <p>Auxiliary effects must be created on session 0 (global output mix). In order for a
    240      * MediaPlayer or AudioTrack to be fed into this effect, they must be explicitely attached to
    241      * this effect and a send level must be specified.
    242      * <p>Use the effect ID returned by {@link #getId()} to designate this particular effect when
    243      * attaching it to the MediaPlayer or AudioTrack.
    244      */
    245     public static final String EFFECT_AUXILIARY = "Auxiliary";
    246 
    247     // --------------------------------------------------------------------------
    248     // Member variables
    249     // --------------------
    250     /**
    251      * Indicates the state of the AudioEffect instance
    252      */
    253     private int mState = STATE_UNINITIALIZED;
    254     /**
    255      * Lock to synchronize access to mState
    256      */
    257     private final Object mStateLock = new Object();
    258     /**
    259      * System wide unique effect ID
    260      */
    261     private int mId;
    262 
    263     // accessed by native methods
    264     private int mNativeAudioEffect;
    265     private int mJniData;
    266 
    267     /**
    268      * Effect descriptor
    269      */
    270     private Descriptor mDescriptor;
    271 
    272     /**
    273      * Listener for effect engine state change notifications.
    274      *
    275      * @see #setEnableStatusListener(OnEnableStatusChangeListener)
    276      */
    277     private OnEnableStatusChangeListener mEnableStatusChangeListener = null;
    278     /**
    279      * Listener for effect engine control ownership change notifications.
    280      *
    281      * @see #setControlStatusListener(OnControlStatusChangeListener)
    282      */
    283     private OnControlStatusChangeListener mControlChangeStatusListener = null;
    284     /**
    285      * Listener for effect engine control ownership change notifications.
    286      *
    287      * @see #setParameterListener(OnParameterChangeListener)
    288      */
    289     private OnParameterChangeListener mParameterChangeListener = null;
    290     /**
    291      * Lock to protect listeners updates against event notifications
    292      * @hide
    293      */
    294     public final Object mListenerLock = new Object();
    295     /**
    296      * Handler for events coming from the native code
    297      * @hide
    298      */
    299     public NativeEventHandler mNativeEventHandler = null;
    300 
    301     // --------------------------------------------------------------------------
    302     // Constructor, Finalize
    303     // --------------------
    304     /**
    305      * Class constructor.
    306      *
    307      * @param type type of effect engine created. See {@link #EFFECT_TYPE_ENV_REVERB},
    308      *            {@link #EFFECT_TYPE_EQUALIZER} ... Types corresponding to
    309      *            built-in effects are defined by AudioEffect class. Other types
    310      *            can be specified provided they correspond an existing OpenSL
    311      *            ES interface ID and the corresponsing effect is available on
    312      *            the platform. If an unspecified effect type is requested, the
    313      *            constructor with throw the IllegalArgumentException. This
    314      *            parameter can be set to {@link #EFFECT_TYPE_NULL} in which
    315      *            case only the uuid will be used to select the effect.
    316      * @param uuid unique identifier of a particular effect implementation.
    317      *            Must be specified if the caller wants to use a particular
    318      *            implementation of an effect type. This parameter can be set to
    319      *            {@link #EFFECT_TYPE_NULL} in which case only the type will
    320      *            be used to select the effect.
    321      * @param priority the priority level requested by the application for
    322      *            controlling the effect engine. As the same effect engine can
    323      *            be shared by several applications, this parameter indicates
    324      *            how much the requesting application needs control of effect
    325      *            parameters. The normal priority is 0, above normal is a
    326      *            positive number, below normal a negative number.
    327      * @param audioSession system wide unique audio session identifier. If audioSession
    328      *            is not 0, the effect will be attached to the MediaPlayer or
    329      *            AudioTrack in the same audio session. Otherwise, the effect
    330      *            will apply to the output mix.
    331      *
    332      * @throws java.lang.IllegalArgumentException
    333      * @throws java.lang.UnsupportedOperationException
    334      * @throws java.lang.RuntimeException
    335      * @hide
    336      */
    337 
    338     public AudioEffect(UUID type, UUID uuid, int priority, int audioSession)
    339             throws IllegalArgumentException, UnsupportedOperationException,
    340             RuntimeException {
    341         int[] id = new int[1];
    342         Descriptor[] desc = new Descriptor[1];
    343         // native initialization
    344         int initResult = native_setup(new WeakReference<AudioEffect>(this),
    345                 type.toString(), uuid.toString(), priority, audioSession, id,
    346                 desc);
    347         if (initResult != SUCCESS && initResult != ALREADY_EXISTS) {
    348             Log.e(TAG, "Error code " + initResult
    349                     + " when initializing AudioEffect.");
    350             switch (initResult) {
    351             case ERROR_BAD_VALUE:
    352                 throw (new IllegalArgumentException("Effect type: " + type
    353                         + " not supported."));
    354             case ERROR_INVALID_OPERATION:
    355                 throw (new UnsupportedOperationException(
    356                         "Effect library not loaded"));
    357             default:
    358                 throw (new RuntimeException(
    359                         "Cannot initialize effect engine for type: " + type
    360                                 + "Error: " + initResult));
    361             }
    362         }
    363         mId = id[0];
    364         mDescriptor = desc[0];
    365         synchronized (mStateLock) {
    366             mState = STATE_INITIALIZED;
    367         }
    368     }
    369 
    370     /**
    371      * Releases the native AudioEffect resources. It is a good practice to
    372      * release the effect engine when not in use as control can be returned to
    373      * other applications or the native resources released.
    374      */
    375     public void release() {
    376         synchronized (mStateLock) {
    377             native_release();
    378             mState = STATE_UNINITIALIZED;
    379         }
    380     }
    381 
    382     @Override
    383     protected void finalize() {
    384         native_finalize();
    385     }
    386 
    387     /**
    388      * Get the effect descriptor.
    389      *
    390      * @see android.media.audiofx.AudioEffect.Descriptor
    391      * @throws IllegalStateException
    392      */
    393     public Descriptor getDescriptor() throws IllegalStateException {
    394         checkState("getDescriptor()");
    395         return mDescriptor;
    396     }
    397 
    398     // --------------------------------------------------------------------------
    399     // Effects Enumeration
    400     // --------------------
    401 
    402     /**
    403      * Query all effects available on the platform. Returns an array of
    404      * {@link android.media.audiofx.AudioEffect.Descriptor} objects
    405      *
    406      * @throws IllegalStateException
    407      */
    408 
    409     static public Descriptor[] queryEffects() {
    410         return (Descriptor[]) native_query_effects();
    411     }
    412 
    413     // --------------------------------------------------------------------------
    414     // Control methods
    415     // --------------------
    416 
    417     /**
    418      * Enable or disable the effect.
    419      * Creating an audio effect does not automatically apply this effect on the audio source. It
    420      * creates the resources necessary to process this effect but the audio signal is still bypassed
    421      * through the effect engine. Calling this method will make that the effect is actually applied
    422      * or not to the audio content being played in the corresponding audio session.
    423      *
    424      * @param enabled the requested enable state
    425      * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION}
    426      *         or {@link #ERROR_DEAD_OBJECT} in case of failure.
    427      * @throws IllegalStateException
    428      */
    429     public int setEnabled(boolean enabled) throws IllegalStateException {
    430         checkState("setEnabled()");
    431         return native_setEnabled(enabled);
    432     }
    433 
    434     /**
    435      * Set effect parameter. The setParameter method is provided in several
    436      * forms addressing most common parameter formats. This form is the most
    437      * generic one where the parameter and its value are both specified as an
    438      * array of bytes. The parameter and value type and length are therefore
    439      * totally free. For standard effect defined by OpenSL ES, the parameter
    440      * format and values must match the definitions in the corresponding OpenSL
    441      * ES interface.
    442      *
    443      * @param param the identifier of the parameter to set
    444      * @param value the new value for the specified parameter
    445      * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE},
    446      *         {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or
    447      *         {@link #ERROR_DEAD_OBJECT} in case of failure
    448      * @throws IllegalStateException
    449      * @hide
    450      */
    451     public int setParameter(byte[] param, byte[] value)
    452             throws IllegalStateException {
    453         checkState("setParameter()");
    454         return native_setParameter(param.length, param, value.length, value);
    455     }
    456 
    457     /**
    458      * Set effect parameter. The parameter and its value are integers.
    459      *
    460      * @see #setParameter(byte[], byte[])
    461      * @hide
    462      */
    463     public int setParameter(int param, int value) throws IllegalStateException {
    464         byte[] p = intToByteArray(param);
    465         byte[] v = intToByteArray(value);
    466         return setParameter(p, v);
    467     }
    468 
    469     /**
    470      * Set effect parameter. The parameter is an integer and the value is a
    471      * short integer.
    472      *
    473      * @see #setParameter(byte[], byte[])
    474      * @hide
    475      */
    476     public int setParameter(int param, short value)
    477             throws IllegalStateException {
    478         byte[] p = intToByteArray(param);
    479         byte[] v = shortToByteArray(value);
    480         return setParameter(p, v);
    481     }
    482 
    483     /**
    484      * Set effect parameter. The parameter is an integer and the value is an
    485      * array of bytes.
    486      *
    487      * @see #setParameter(byte[], byte[])
    488      * @hide
    489      */
    490     public int setParameter(int param, byte[] value)
    491             throws IllegalStateException {
    492         byte[] p = intToByteArray(param);
    493         return setParameter(p, value);
    494     }
    495 
    496     /**
    497      * Set effect parameter. The parameter is an array of 1 or 2 integers and
    498      * the value is also an array of 1 or 2 integers
    499      *
    500      * @see #setParameter(byte[], byte[])
    501      * @hide
    502      */
    503     public int setParameter(int[] param, int[] value)
    504             throws IllegalStateException {
    505         if (param.length > 2 || value.length > 2) {
    506             return ERROR_BAD_VALUE;
    507         }
    508         byte[] p = intToByteArray(param[0]);
    509         if (param.length > 1) {
    510             byte[] p2 = intToByteArray(param[1]);
    511             p = concatArrays(p, p2);
    512         }
    513         byte[] v = intToByteArray(value[0]);
    514         if (value.length > 1) {
    515             byte[] v2 = intToByteArray(value[1]);
    516             v = concatArrays(v, v2);
    517         }
    518         return setParameter(p, v);
    519     }
    520 
    521     /**
    522      * Set effect parameter. The parameter is an array of 1 or 2 integers and
    523      * the value is an array of 1 or 2 short integers
    524      *
    525      * @see #setParameter(byte[], byte[])
    526      * @hide
    527      */
    528     public int setParameter(int[] param, short[] value)
    529             throws IllegalStateException {
    530         if (param.length > 2 || value.length > 2) {
    531             return ERROR_BAD_VALUE;
    532         }
    533         byte[] p = intToByteArray(param[0]);
    534         if (param.length > 1) {
    535             byte[] p2 = intToByteArray(param[1]);
    536             p = concatArrays(p, p2);
    537         }
    538 
    539         byte[] v = shortToByteArray(value[0]);
    540         if (value.length > 1) {
    541             byte[] v2 = shortToByteArray(value[1]);
    542             v = concatArrays(v, v2);
    543         }
    544         return setParameter(p, v);
    545     }
    546 
    547     /**
    548      * Set effect parameter. The parameter is an array of 1 or 2 integers and
    549      * the value is an array of bytes
    550      *
    551      * @see #setParameter(byte[], byte[])
    552      * @hide
    553      */
    554     public int setParameter(int[] param, byte[] value)
    555             throws IllegalStateException {
    556         if (param.length > 2) {
    557             return ERROR_BAD_VALUE;
    558         }
    559         byte[] p = intToByteArray(param[0]);
    560         if (param.length > 1) {
    561             byte[] p2 = intToByteArray(param[1]);
    562             p = concatArrays(p, p2);
    563         }
    564         return setParameter(p, value);
    565     }
    566 
    567     /**
    568      * Get effect parameter. The getParameter method is provided in several
    569      * forms addressing most common parameter formats. This form is the most
    570      * generic one where the parameter and its value are both specified as an
    571      * array of bytes. The parameter and value type and length are therefore
    572      * totally free.
    573      *
    574      * @param param the identifier of the parameter to set
    575      * @param value the new value for the specified parameter
    576      * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE},
    577      *         {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or
    578      *         {@link #ERROR_DEAD_OBJECT} in case of failure When called, value.length
    579      *         indicates the maximum size of the returned parameters value. When
    580      *         returning, value.length is updated with the actual size of the
    581      *         returned value.
    582      * @throws IllegalStateException
    583      * @hide
    584      */
    585     public int getParameter(byte[] param, byte[] value)
    586             throws IllegalStateException {
    587         checkState("getParameter()");
    588         int[] vSize = new int[1];
    589         vSize[0] = value.length;
    590         int status = native_getParameter(param.length, param, vSize, value);
    591         if (value.length > vSize[0]) {
    592             byte[] resizedValue = new byte[vSize[0]];
    593             System.arraycopy(value, 0, resizedValue, 0, vSize[0]);
    594             value = resizedValue;
    595         }
    596         return status;
    597     }
    598 
    599     /**
    600      * Get effect parameter. The parameter is an integer and the value is an
    601      * array of bytes.
    602      *
    603      * @see #getParameter(byte[], byte[])
    604      * @hide
    605      */
    606     public int getParameter(int param, byte[] value)
    607             throws IllegalStateException {
    608         byte[] p = intToByteArray(param);
    609 
    610         return getParameter(p, value);
    611     }
    612 
    613     /**
    614      * Get effect parameter. The parameter is an integer and the value is an
    615      * array of 1 or 2 integers
    616      *
    617      * @see #getParameter(byte[], byte[])
    618      * @hide
    619      */
    620     public int getParameter(int param, int[] value)
    621             throws IllegalStateException {
    622         if (value.length > 2) {
    623             return ERROR_BAD_VALUE;
    624         }
    625         byte[] p = intToByteArray(param);
    626 
    627         byte[] v = new byte[value.length * 4];
    628 
    629         int status = getParameter(p, v);
    630 
    631         value[0] = byteArrayToInt(v);
    632         if (v.length > 4) {
    633             value[1] = byteArrayToInt(v, 4);
    634         }
    635         return status;
    636     }
    637 
    638     /**
    639      * Get effect parameter. The parameter is an integer and the value is an
    640      * array of 1 or 2 short integers
    641      *
    642      * @see #getParameter(byte[], byte[])
    643      * @hide
    644      */
    645     public int getParameter(int param, short[] value)
    646             throws IllegalStateException {
    647         if (value.length > 2) {
    648             return ERROR_BAD_VALUE;
    649         }
    650         byte[] p = intToByteArray(param);
    651 
    652         byte[] v = new byte[value.length * 2];
    653 
    654         int status = getParameter(p, v);
    655 
    656         value[0] = byteArrayToShort(v);
    657         if (v.length > 2) {
    658             value[1] = byteArrayToShort(v, 2);
    659         }
    660         return status;
    661     }
    662 
    663     /**
    664      * Get effect parameter. The parameter is an array of 1 or 2 integers and
    665      * the value is also an array of 1 or 2 integers
    666      *
    667      * @see #getParameter(byte[], byte[])
    668      * @hide
    669      */
    670     public int getParameter(int[] param, int[] value)
    671             throws IllegalStateException {
    672         if (param.length > 2 || value.length > 2) {
    673             return ERROR_BAD_VALUE;
    674         }
    675         byte[] p = intToByteArray(param[0]);
    676         if (param.length > 1) {
    677             byte[] p2 = intToByteArray(param[1]);
    678             p = concatArrays(p, p2);
    679         }
    680         byte[] v = new byte[value.length * 4];
    681 
    682         int status = getParameter(p, v);
    683 
    684         value[0] = byteArrayToInt(v);
    685         if (v.length > 4) {
    686             value[1] = byteArrayToInt(v, 4);
    687         }
    688         return status;
    689     }
    690 
    691     /**
    692      * Get effect parameter. The parameter is an array of 1 or 2 integers and
    693      * the value is an array of 1 or 2 short integers
    694      *
    695      * @see #getParameter(byte[], byte[])
    696      * @hide
    697      */
    698     public int getParameter(int[] param, short[] value)
    699             throws IllegalStateException {
    700         if (param.length > 2 || value.length > 2) {
    701             return ERROR_BAD_VALUE;
    702         }
    703         byte[] p = intToByteArray(param[0]);
    704         if (param.length > 1) {
    705             byte[] p2 = intToByteArray(param[1]);
    706             p = concatArrays(p, p2);
    707         }
    708         byte[] v = new byte[value.length * 2];
    709 
    710         int status = getParameter(p, v);
    711 
    712         value[0] = byteArrayToShort(v);
    713         if (v.length > 2) {
    714             value[1] = byteArrayToShort(v, 2);
    715         }
    716         return status;
    717     }
    718 
    719     /**
    720      * Get effect parameter. The parameter is an array of 1 or 2 integers and
    721      * the value is an array of bytes
    722      *
    723      * @see #getParameter(byte[], byte[])
    724      * @hide
    725      */
    726     public int getParameter(int[] param, byte[] value)
    727             throws IllegalStateException {
    728         if (param.length > 2) {
    729             return ERROR_BAD_VALUE;
    730         }
    731         byte[] p = intToByteArray(param[0]);
    732         if (param.length > 1) {
    733             byte[] p2 = intToByteArray(param[1]);
    734             p = concatArrays(p, p2);
    735         }
    736 
    737         return getParameter(p, value);
    738     }
    739 
    740     /**
    741      * Send a command to the effect engine. This method is intended to send
    742      * proprietary commands to a particular effect implementation.
    743      *
    744      * @hide
    745      */
    746     public int command(int cmdCode, byte[] command, byte[] reply)
    747             throws IllegalStateException {
    748         checkState("command()");
    749         int[] replySize = new int[1];
    750         replySize[0] = reply.length;
    751 
    752         int status = native_command(cmdCode, command.length, command,
    753                 replySize, reply);
    754 
    755         if (reply.length > replySize[0]) {
    756             byte[] resizedReply = new byte[replySize[0]];
    757             System.arraycopy(reply, 0, resizedReply, 0, replySize[0]);
    758             reply = resizedReply;
    759         }
    760         return status;
    761     }
    762 
    763     // --------------------------------------------------------------------------
    764     // Getters
    765     // --------------------
    766 
    767     /**
    768      * Returns effect unique identifier. This system wide unique identifier can
    769      * be used to attach this effect to a MediaPlayer or an AudioTrack when the
    770      * effect is an auxiliary effect (Reverb)
    771      *
    772      * @return the effect identifier.
    773      * @throws IllegalStateException
    774      */
    775     public int getId() throws IllegalStateException {
    776         checkState("getId()");
    777         return mId;
    778     }
    779 
    780     /**
    781      * Returns effect enabled state
    782      *
    783      * @return true if the effect is enabled, false otherwise.
    784      * @throws IllegalStateException
    785      */
    786     public boolean getEnabled() throws IllegalStateException {
    787         checkState("getEnabled()");
    788         return native_getEnabled();
    789     }
    790 
    791     /**
    792      * Checks if this AudioEffect object is controlling the effect engine.
    793      *
    794      * @return true if this instance has control of effect engine, false
    795      *         otherwise.
    796      * @throws IllegalStateException
    797      */
    798     public boolean hasControl() throws IllegalStateException {
    799         checkState("hasControl()");
    800         return native_hasControl();
    801     }
    802 
    803     // --------------------------------------------------------------------------
    804     // Initialization / configuration
    805     // --------------------
    806     /**
    807      * Sets the listener AudioEffect notifies when the effect engine is enabled
    808      * or disabled.
    809      *
    810      * @param listener
    811      */
    812     public void setEnableStatusListener(OnEnableStatusChangeListener listener) {
    813         synchronized (mListenerLock) {
    814             mEnableStatusChangeListener = listener;
    815         }
    816         if ((listener != null) && (mNativeEventHandler == null)) {
    817             createNativeEventHandler();
    818         }
    819     }
    820 
    821     /**
    822      * Sets the listener AudioEffect notifies when the effect engine control is
    823      * taken or returned.
    824      *
    825      * @param listener
    826      */
    827     public void setControlStatusListener(OnControlStatusChangeListener listener) {
    828         synchronized (mListenerLock) {
    829             mControlChangeStatusListener = listener;
    830         }
    831         if ((listener != null) && (mNativeEventHandler == null)) {
    832             createNativeEventHandler();
    833         }
    834     }
    835 
    836     /**
    837      * Sets the listener AudioEffect notifies when a parameter is changed.
    838      *
    839      * @param listener
    840      * @hide
    841      */
    842     public void setParameterListener(OnParameterChangeListener listener) {
    843         synchronized (mListenerLock) {
    844             mParameterChangeListener = listener;
    845         }
    846         if ((listener != null) && (mNativeEventHandler == null)) {
    847             createNativeEventHandler();
    848         }
    849     }
    850 
    851     // Convenience method for the creation of the native event handler
    852     // It is called only when a non-null event listener is set.
    853     // precondition:
    854     // mNativeEventHandler is null
    855     private void createNativeEventHandler() {
    856         Looper looper;
    857         if ((looper = Looper.myLooper()) != null) {
    858             mNativeEventHandler = new NativeEventHandler(this, looper);
    859         } else if ((looper = Looper.getMainLooper()) != null) {
    860             mNativeEventHandler = new NativeEventHandler(this, looper);
    861         } else {
    862             mNativeEventHandler = null;
    863         }
    864     }
    865 
    866     // ---------------------------------------------------------
    867     // Interface definitions
    868     // --------------------
    869     /**
    870      * The OnEnableStatusChangeListener interface defines a method called by the AudioEffect
    871      * when a the enabled state of the effect engine was changed by the controlling application.
    872      */
    873     public interface OnEnableStatusChangeListener {
    874         /**
    875          * Called on the listener to notify it that the effect engine has been
    876          * enabled or disabled.
    877          * @param effect the effect on which the interface is registered.
    878          * @param enabled new effect state.
    879          */
    880         void onEnableStatusChange(AudioEffect effect, boolean enabled);
    881     }
    882 
    883     /**
    884      * The OnControlStatusChangeListener interface defines a method called by the AudioEffect
    885      * when a the control of the effect engine is gained or lost by the application
    886      */
    887     public interface OnControlStatusChangeListener {
    888         /**
    889          * Called on the listener to notify it that the effect engine control
    890          * has been taken or returned.
    891          * @param effect the effect on which the interface is registered.
    892          * @param controlGranted true if the application has been granted control of the effect
    893          * engine, false otherwise.
    894          */
    895         void onControlStatusChange(AudioEffect effect, boolean controlGranted);
    896     }
    897 
    898     /**
    899      * The OnParameterChangeListener interface defines a method called by the AudioEffect
    900      * when a parameter is changed in the effect engine by the controlling application.
    901      * @hide
    902      */
    903     public interface OnParameterChangeListener {
    904         /**
    905          * Called on the listener to notify it that a parameter value has changed.
    906          * @param effect the effect on which the interface is registered.
    907          * @param status status of the set parameter operation.
    908          * @param param ID of the modified parameter.
    909          * @param value the new parameter value.
    910          */
    911         void onParameterChange(AudioEffect effect, int status, byte[] param,
    912                 byte[] value);
    913     }
    914 
    915 
    916     // -------------------------------------------------------------------------
    917     // Audio Effect Control panel intents
    918     // -------------------------------------------------------------------------
    919 
    920     /**
    921      *  Intent to launch an audio effect control panel UI.
    922      *  <p>The goal of this intent is to enable separate implementations of music/media player
    923      *  applications and audio effect control application or services.
    924      *  This will allow platform vendors to offer more advanced control options for standard effects
    925      *  or control for platform specific effects.
    926      *  <p>The intent carries a number of extras used by the player application to communicate
    927      *  necessary pieces of information to the control panel application.
    928      *  <p>The calling application must use the
    929      *  {@link android.app.Activity#startActivityForResult(Intent, int)} method to launch the
    930      *  control panel so that its package name is indicated and used by the control panel
    931      *  application to keep track of changes for this particular application.
    932      *  <p>The {@link #EXTRA_AUDIO_SESSION} extra will indicate an audio session to which the
    933      *  audio effects should be applied. If no audio session is specified, either one of the
    934      *  follownig will happen:
    935      *  <p>- If an audio session was previously opened by the calling application with
    936      *  {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intent, the effect changes will
    937      *  be applied to that session.
    938      *  <p>- If no audio session is opened, the changes will be stored in the package specific
    939      *  storage area and applied whenever a new audio session is opened by this application.
    940      *  <p>The {@link #EXTRA_CONTENT_TYPE} extra will help the control panel application
    941      *  customize both the UI layout and the default audio effect settings if none are already
    942      *  stored for the calling application.
    943      */
    944     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    945     public static final String ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL =
    946         "android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL";
    947 
    948     /**
    949      *  Intent to signal to the effect control application or service that a new audio session
    950      *  is opened and requires audio effects to be applied.
    951      *  <p>This is different from {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} in that no
    952      *  UI should be displayed in this case. Music player applications can broadcast this intent
    953      *  before starting playback to make sure that any audio effect settings previously selected
    954      *  by the user are applied.
    955      *  <p>The effect control application receiving this intent will look for previously stored
    956      *  settings for the calling application, create all required audio effects and apply the
    957      *  effect settings to the specified audio session.
    958      *  <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the
    959      *  audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory.
    960      *  <p>If no stored settings are found for the calling application, default settings for the
    961      *  content type indicated by {@link #EXTRA_CONTENT_TYPE} will be applied. The default settings
    962      *  for a given content type are platform specific.
    963      */
    964     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    965     public static final String ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION =
    966         "android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION";
    967 
    968     /**
    969      *  Intent to signal to the effect control application or service that an audio session
    970      *  is closed and that effects should not be applied anymore.
    971      *  <p>The effect control application receiving this intent will delete all effects on
    972      *  this session and store current settings in package specific storage.
    973      *  <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the
    974      *  audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory.
    975      *  <p>It is good practice for applications to broadcast this intent when music playback stops
    976      *  and/or when exiting to free system resources consumed by audio effect engines.
    977      */
    978     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    979     public static final String ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION =
    980         "android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION";
    981 
    982     /**
    983      * Contains the ID of the audio session the effects should be applied to.
    984      * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL},
    985      * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
    986      * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
    987      * <p>The extra value is of type int and is the audio session ID.
    988      *  @see android.media.MediaPlayer#getAudioSessionId() for details on audio sessions.
    989      */
    990      public static final String EXTRA_AUDIO_SESSION = "android.media.extra.AUDIO_SESSION";
    991 
    992     /**
    993      * Contains the package name of the calling application.
    994      * <p>This extra is for use with {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
    995      * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
    996      * <p>The extra value is a string containing the full package name.
    997      */
    998     public static final String EXTRA_PACKAGE_NAME = "android.media.extra.PACKAGE_NAME";
    999 
   1000     /**
   1001      * Indicates which type of content is played by the application.
   1002      * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} and
   1003      * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intents.
   1004      * <p>This information is used by the effect control application to customize UI and select
   1005      * appropriate default effect settings. The content type is one of the following:
   1006      * <ul>
   1007      *   <li>{@link #CONTENT_TYPE_MUSIC}</li>
   1008      *   <li>{@link #CONTENT_TYPE_MOVIE}</li>
   1009      *   <li>{@link #CONTENT_TYPE_GAME}</li>
   1010      *   <li>{@link #CONTENT_TYPE_VOICE}</li>
   1011      * </ul>
   1012      * If omitted, the content type defaults to {@link #CONTENT_TYPE_MUSIC}.
   1013      */
   1014     public static final String EXTRA_CONTENT_TYPE = "android.media.extra.CONTENT_TYPE";
   1015 
   1016     /**
   1017      * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is music
   1018      */
   1019     public static final int  CONTENT_TYPE_MUSIC = 0;
   1020     /**
   1021      * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is video or movie
   1022      */
   1023     public static final int  CONTENT_TYPE_MOVIE = 1;
   1024     /**
   1025      * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is game audio
   1026      */
   1027     public static final int  CONTENT_TYPE_GAME = 2;
   1028     /**
   1029      * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is voice audio
   1030      */
   1031     public static final int  CONTENT_TYPE_VOICE = 3;
   1032 
   1033 
   1034     // ---------------------------------------------------------
   1035     // Inner classes
   1036     // --------------------
   1037     /**
   1038      * Helper class to handle the forwarding of native events to the appropriate
   1039      * listeners
   1040      */
   1041     private class NativeEventHandler extends Handler {
   1042         private AudioEffect mAudioEffect;
   1043 
   1044         public NativeEventHandler(AudioEffect ae, Looper looper) {
   1045             super(looper);
   1046             mAudioEffect = ae;
   1047         }
   1048 
   1049         @Override
   1050         public void handleMessage(Message msg) {
   1051             if (mAudioEffect == null) {
   1052                 return;
   1053             }
   1054             switch (msg.what) {
   1055             case NATIVE_EVENT_ENABLED_STATUS:
   1056                 OnEnableStatusChangeListener enableStatusChangeListener = null;
   1057                 synchronized (mListenerLock) {
   1058                     enableStatusChangeListener = mAudioEffect.mEnableStatusChangeListener;
   1059                 }
   1060                 if (enableStatusChangeListener != null) {
   1061                     enableStatusChangeListener.onEnableStatusChange(
   1062                             mAudioEffect, (boolean) (msg.arg1 != 0));
   1063                 }
   1064                 break;
   1065             case NATIVE_EVENT_CONTROL_STATUS:
   1066                 OnControlStatusChangeListener controlStatusChangeListener = null;
   1067                 synchronized (mListenerLock) {
   1068                     controlStatusChangeListener = mAudioEffect.mControlChangeStatusListener;
   1069                 }
   1070                 if (controlStatusChangeListener != null) {
   1071                     controlStatusChangeListener.onControlStatusChange(
   1072                             mAudioEffect, (boolean) (msg.arg1 != 0));
   1073                 }
   1074                 break;
   1075             case NATIVE_EVENT_PARAMETER_CHANGED:
   1076                 OnParameterChangeListener parameterChangeListener = null;
   1077                 synchronized (mListenerLock) {
   1078                     parameterChangeListener = mAudioEffect.mParameterChangeListener;
   1079                 }
   1080                 if (parameterChangeListener != null) {
   1081                     // arg1 contains offset of parameter value from start of
   1082                     // byte array
   1083                     int vOffset = msg.arg1;
   1084                     byte[] p = (byte[]) msg.obj;
   1085                     // See effect_param_t in EffectApi.h for psize and vsize
   1086                     // fields offsets
   1087                     int status = byteArrayToInt(p, 0);
   1088                     int psize = byteArrayToInt(p, 4);
   1089                     int vsize = byteArrayToInt(p, 8);
   1090                     byte[] param = new byte[psize];
   1091                     byte[] value = new byte[vsize];
   1092                     System.arraycopy(p, 12, param, 0, psize);
   1093                     System.arraycopy(p, vOffset, value, 0, vsize);
   1094 
   1095                     parameterChangeListener.onParameterChange(mAudioEffect,
   1096                             status, param, value);
   1097                 }
   1098                 break;
   1099 
   1100             default:
   1101                 Log.e(TAG, "handleMessage() Unknown event type: " + msg.what);
   1102                 break;
   1103             }
   1104         }
   1105     }
   1106 
   1107     // ---------------------------------------------------------
   1108     // Java methods called from the native side
   1109     // --------------------
   1110     @SuppressWarnings("unused")
   1111     private static void postEventFromNative(Object effect_ref, int what,
   1112             int arg1, int arg2, Object obj) {
   1113         AudioEffect effect = (AudioEffect) ((WeakReference) effect_ref).get();
   1114         if (effect == null) {
   1115             return;
   1116         }
   1117         if (effect.mNativeEventHandler != null) {
   1118             Message m = effect.mNativeEventHandler.obtainMessage(what, arg1,
   1119                     arg2, obj);
   1120             effect.mNativeEventHandler.sendMessage(m);
   1121         }
   1122 
   1123     }
   1124 
   1125     // ---------------------------------------------------------
   1126     // Native methods called from the Java side
   1127     // --------------------
   1128 
   1129     private static native final void native_init();
   1130 
   1131     private native final int native_setup(Object audioeffect_this, String type,
   1132             String uuid, int priority, int audioSession, int[] id, Object[] desc);
   1133 
   1134     private native final void native_finalize();
   1135 
   1136     private native final void native_release();
   1137 
   1138     private native final int native_setEnabled(boolean enabled);
   1139 
   1140     private native final boolean native_getEnabled();
   1141 
   1142     private native final boolean native_hasControl();
   1143 
   1144     private native final int native_setParameter(int psize, byte[] param,
   1145             int vsize, byte[] value);
   1146 
   1147     private native final int native_getParameter(int psize, byte[] param,
   1148             int[] vsize, byte[] value);
   1149 
   1150     private native final int native_command(int cmdCode, int cmdSize,
   1151             byte[] cmdData, int[] repSize, byte[] repData);
   1152 
   1153     private static native Object[] native_query_effects();
   1154 
   1155     // ---------------------------------------------------------
   1156     // Utility methods
   1157     // ------------------
   1158 
   1159     /**
   1160     * @hide
   1161     */
   1162     public void checkState(String methodName) throws IllegalStateException {
   1163         synchronized (mStateLock) {
   1164             if (mState != STATE_INITIALIZED) {
   1165                 throw (new IllegalStateException(methodName
   1166                         + " called on uninitialized AudioEffect."));
   1167             }
   1168         }
   1169     }
   1170 
   1171     /**
   1172      * @hide
   1173      */
   1174     public void checkStatus(int status) {
   1175         switch (status) {
   1176         case AudioEffect.SUCCESS:
   1177             break;
   1178         case AudioEffect.ERROR_BAD_VALUE:
   1179             throw (new IllegalArgumentException(
   1180                     "AudioEffect: bad parameter value"));
   1181         case AudioEffect.ERROR_INVALID_OPERATION:
   1182             throw (new UnsupportedOperationException(
   1183                     "AudioEffect: invalid parameter operation"));
   1184         default:
   1185             throw (new RuntimeException("AudioEffect: set/get parameter error"));
   1186         }
   1187     }
   1188 
   1189     /**
   1190      * @hide
   1191      */
   1192     public int byteArrayToInt(byte[] valueBuf) {
   1193         return byteArrayToInt(valueBuf, 0);
   1194 
   1195     }
   1196 
   1197     /**
   1198      * @hide
   1199      */
   1200     public int byteArrayToInt(byte[] valueBuf, int offset) {
   1201         ByteBuffer converter = ByteBuffer.wrap(valueBuf);
   1202         converter.order(ByteOrder.nativeOrder());
   1203         return converter.getInt(offset);
   1204 
   1205     }
   1206 
   1207     /**
   1208      * @hide
   1209      */
   1210     public byte[] intToByteArray(int value) {
   1211         ByteBuffer converter = ByteBuffer.allocate(4);
   1212         converter.order(ByteOrder.nativeOrder());
   1213         converter.putInt(value);
   1214         return converter.array();
   1215     }
   1216 
   1217     /**
   1218      * @hide
   1219      */
   1220     public short byteArrayToShort(byte[] valueBuf) {
   1221         return byteArrayToShort(valueBuf, 0);
   1222     }
   1223 
   1224     /**
   1225      * @hide
   1226      */
   1227     public short byteArrayToShort(byte[] valueBuf, int offset) {
   1228         ByteBuffer converter = ByteBuffer.wrap(valueBuf);
   1229         converter.order(ByteOrder.nativeOrder());
   1230         return converter.getShort(offset);
   1231 
   1232     }
   1233 
   1234     /**
   1235      * @hide
   1236      */
   1237     public byte[] shortToByteArray(short value) {
   1238         ByteBuffer converter = ByteBuffer.allocate(2);
   1239         converter.order(ByteOrder.nativeOrder());
   1240         short sValue = (short) value;
   1241         converter.putShort(sValue);
   1242         return converter.array();
   1243     }
   1244 
   1245     /**
   1246      * @hide
   1247      */
   1248     public byte[] concatArrays(byte[]... arrays) {
   1249         int len = 0;
   1250         for (byte[] a : arrays) {
   1251             len += a.length;
   1252         }
   1253         byte[] b = new byte[len];
   1254 
   1255         int offs = 0;
   1256         for (byte[] a : arrays) {
   1257             System.arraycopy(a, 0, b, offs, a.length);
   1258             offs += a.length;
   1259         }
   1260         return b;
   1261     }
   1262 }
   1263