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