AudioEffect.java revision 855255d89fe0a14abe796355bebb64031ec6ff47
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     * Checks if the device implements the specified effect type.
457     * @param type the requested effect type.
458     * @return true if the device implements the specified effect type, false otherwise.
459     * @hide
460     */
461    public static boolean isEffectTypeAvailable(UUID type) {
462        AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
463        for (int i = 0; i < desc.length; i++) {
464            if (desc[i].type.equals(type)) {
465                return true;
466            }
467        }
468        return false;
469    }
470
471    // --------------------------------------------------------------------------
472    // Control methods
473    // --------------------
474
475    /**
476     * Enable or disable the effect.
477     * Creating an audio effect does not automatically apply this effect on the audio source. It
478     * creates the resources necessary to process this effect but the audio signal is still bypassed
479     * through the effect engine. Calling this method will make that the effect is actually applied
480     * or not to the audio content being played in the corresponding audio session.
481     *
482     * @param enabled the requested enable state
483     * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION}
484     *         or {@link #ERROR_DEAD_OBJECT} in case of failure.
485     * @throws IllegalStateException
486     */
487    public int setEnabled(boolean enabled) throws IllegalStateException {
488        checkState("setEnabled()");
489        return native_setEnabled(enabled);
490    }
491
492    /**
493     * Set effect parameter. The setParameter method is provided in several
494     * forms addressing most common parameter formats. This form is the most
495     * generic one where the parameter and its value are both specified as an
496     * array of bytes. The parameter and value type and length are therefore
497     * totally free. For standard effect defined by OpenSL ES, the parameter
498     * format and values must match the definitions in the corresponding OpenSL
499     * ES interface.
500     *
501     * @param param the identifier of the parameter to set
502     * @param value the new value for the specified parameter
503     * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE},
504     *         {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or
505     *         {@link #ERROR_DEAD_OBJECT} in case of failure
506     * @throws IllegalStateException
507     * @hide
508     */
509    public int setParameter(byte[] param, byte[] value)
510            throws IllegalStateException {
511        checkState("setParameter()");
512        return native_setParameter(param.length, param, value.length, value);
513    }
514
515    /**
516     * Set effect parameter. The parameter and its value are integers.
517     *
518     * @see #setParameter(byte[], byte[])
519     * @hide
520     */
521    public int setParameter(int param, int value) throws IllegalStateException {
522        byte[] p = intToByteArray(param);
523        byte[] v = intToByteArray(value);
524        return setParameter(p, v);
525    }
526
527    /**
528     * Set effect parameter. The parameter is an integer and the value is a
529     * short integer.
530     *
531     * @see #setParameter(byte[], byte[])
532     * @hide
533     */
534    public int setParameter(int param, short value)
535            throws IllegalStateException {
536        byte[] p = intToByteArray(param);
537        byte[] v = shortToByteArray(value);
538        return setParameter(p, v);
539    }
540
541    /**
542     * Set effect parameter. The parameter is an integer and the value is an
543     * array of bytes.
544     *
545     * @see #setParameter(byte[], byte[])
546     * @hide
547     */
548    public int setParameter(int param, byte[] value)
549            throws IllegalStateException {
550        byte[] p = intToByteArray(param);
551        return setParameter(p, value);
552    }
553
554    /**
555     * Set effect parameter. The parameter is an array of 1 or 2 integers and
556     * the value is also an array of 1 or 2 integers
557     *
558     * @see #setParameter(byte[], byte[])
559     * @hide
560     */
561    public int setParameter(int[] param, int[] value)
562            throws IllegalStateException {
563        if (param.length > 2 || value.length > 2) {
564            return ERROR_BAD_VALUE;
565        }
566        byte[] p = intToByteArray(param[0]);
567        if (param.length > 1) {
568            byte[] p2 = intToByteArray(param[1]);
569            p = concatArrays(p, p2);
570        }
571        byte[] v = intToByteArray(value[0]);
572        if (value.length > 1) {
573            byte[] v2 = intToByteArray(value[1]);
574            v = concatArrays(v, v2);
575        }
576        return setParameter(p, v);
577    }
578
579    /**
580     * Set effect parameter. The parameter is an array of 1 or 2 integers and
581     * the value is an array of 1 or 2 short integers
582     *
583     * @see #setParameter(byte[], byte[])
584     * @hide
585     */
586    public int setParameter(int[] param, short[] value)
587            throws IllegalStateException {
588        if (param.length > 2 || value.length > 2) {
589            return ERROR_BAD_VALUE;
590        }
591        byte[] p = intToByteArray(param[0]);
592        if (param.length > 1) {
593            byte[] p2 = intToByteArray(param[1]);
594            p = concatArrays(p, p2);
595        }
596
597        byte[] v = shortToByteArray(value[0]);
598        if (value.length > 1) {
599            byte[] v2 = shortToByteArray(value[1]);
600            v = concatArrays(v, v2);
601        }
602        return setParameter(p, v);
603    }
604
605    /**
606     * Set effect parameter. The parameter is an array of 1 or 2 integers and
607     * the value is an array of bytes
608     *
609     * @see #setParameter(byte[], byte[])
610     * @hide
611     */
612    public int setParameter(int[] param, byte[] value)
613            throws IllegalStateException {
614        if (param.length > 2) {
615            return ERROR_BAD_VALUE;
616        }
617        byte[] p = intToByteArray(param[0]);
618        if (param.length > 1) {
619            byte[] p2 = intToByteArray(param[1]);
620            p = concatArrays(p, p2);
621        }
622        return setParameter(p, value);
623    }
624
625    /**
626     * Get effect parameter. The getParameter method is provided in several
627     * forms addressing most common parameter formats. This form is the most
628     * generic one where the parameter and its value are both specified as an
629     * array of bytes. The parameter and value type and length are therefore
630     * totally free.
631     *
632     * @param param the identifier of the parameter to set
633     * @param value the new value for the specified parameter
634     * @return the number of meaningful bytes in value array in case of success or
635     *  {@link #ERROR_BAD_VALUE}, {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION}
636     *  or {@link #ERROR_DEAD_OBJECT} in case of failure.
637     * @throws IllegalStateException
638     * @hide
639     */
640    public int getParameter(byte[] param, byte[] value)
641            throws IllegalStateException {
642        checkState("getParameter()");
643        return native_getParameter(param.length, param, value.length, value);
644    }
645
646    /**
647     * Get effect parameter. The parameter is an integer and the value is an
648     * array of bytes.
649     *
650     * @see #getParameter(byte[], byte[])
651     * @hide
652     */
653    public int getParameter(int param, byte[] value)
654            throws IllegalStateException {
655        byte[] p = intToByteArray(param);
656
657        return getParameter(p, value);
658    }
659
660    /**
661     * Get effect parameter. The parameter is an integer and the value is an
662     * array of 1 or 2 integers
663     *
664     * @see #getParameter(byte[], byte[])
665     * In case of success, returns the number of meaningful integers in value array.
666     * @hide
667     */
668    public int getParameter(int param, int[] value)
669            throws IllegalStateException {
670        if (value.length > 2) {
671            return ERROR_BAD_VALUE;
672        }
673        byte[] p = intToByteArray(param);
674
675        byte[] v = new byte[value.length * 4];
676
677        int status = getParameter(p, v);
678
679        if (status == 4 || status == 8) {
680            value[0] = byteArrayToInt(v);
681            if (status == 8) {
682                value[1] = byteArrayToInt(v, 4);
683            }
684            status /= 4;
685        } else {
686            status = ERROR;
687        }
688        return status;
689    }
690
691    /**
692     * Get effect parameter. The parameter is an integer and the value is an
693     * array of 1 or 2 short integers
694     *
695     * @see #getParameter(byte[], byte[])
696     * In case of success, returns the number of meaningful short integers in value array.
697     * @hide
698     */
699    public int getParameter(int param, short[] value)
700            throws IllegalStateException {
701        if (value.length > 2) {
702            return ERROR_BAD_VALUE;
703        }
704        byte[] p = intToByteArray(param);
705
706        byte[] v = new byte[value.length * 2];
707
708        int status = getParameter(p, v);
709
710        if (status == 2 || status == 4) {
711            value[0] = byteArrayToShort(v);
712            if (status == 4) {
713                value[1] = byteArrayToShort(v, 2);
714            }
715            status /= 2;
716        } else {
717            status = ERROR;
718        }
719        return status;
720    }
721
722    /**
723     * Get effect parameter. The parameter is an array of 1 or 2 integers and
724     * the value is also an array of 1 or 2 integers
725     *
726     * @see #getParameter(byte[], byte[])
727     * In case of success, the returns the number of meaningful integers in value array.
728     * @hide
729     */
730    public int getParameter(int[] param, int[] value)
731            throws IllegalStateException {
732        if (param.length > 2 || value.length > 2) {
733            return ERROR_BAD_VALUE;
734        }
735        byte[] p = intToByteArray(param[0]);
736        if (param.length > 1) {
737            byte[] p2 = intToByteArray(param[1]);
738            p = concatArrays(p, p2);
739        }
740        byte[] v = new byte[value.length * 4];
741
742        int status = getParameter(p, v);
743
744        if (status == 4 || status == 8) {
745            value[0] = byteArrayToInt(v);
746            if (status == 8) {
747                value[1] = byteArrayToInt(v, 4);
748            }
749            status /= 4;
750        } else {
751            status = ERROR;
752        }
753        return status;
754    }
755
756    /**
757     * Get effect parameter. The parameter is an array of 1 or 2 integers and
758     * the value is an array of 1 or 2 short integers
759     *
760     * @see #getParameter(byte[], byte[])
761     * In case of success, returns the number of meaningful short integers in value array.
762     * @hide
763     */
764    public int getParameter(int[] param, short[] value)
765            throws IllegalStateException {
766        if (param.length > 2 || value.length > 2) {
767            return ERROR_BAD_VALUE;
768        }
769        byte[] p = intToByteArray(param[0]);
770        if (param.length > 1) {
771            byte[] p2 = intToByteArray(param[1]);
772            p = concatArrays(p, p2);
773        }
774        byte[] v = new byte[value.length * 2];
775
776        int status = getParameter(p, v);
777
778        if (status == 2 || status == 4) {
779            value[0] = byteArrayToShort(v);
780            if (status == 4) {
781                value[1] = byteArrayToShort(v, 2);
782            }
783            status /= 2;
784        } else {
785            status = ERROR;
786        }
787        return status;
788    }
789
790    /**
791     * Get effect parameter. The parameter is an array of 1 or 2 integers and
792     * the value is an array of bytes
793     *
794     * @see #getParameter(byte[], byte[])
795     * @hide
796     */
797    public int getParameter(int[] param, byte[] value)
798            throws IllegalStateException {
799        if (param.length > 2) {
800            return ERROR_BAD_VALUE;
801        }
802        byte[] p = intToByteArray(param[0]);
803        if (param.length > 1) {
804            byte[] p2 = intToByteArray(param[1]);
805            p = concatArrays(p, p2);
806        }
807
808        return getParameter(p, value);
809    }
810
811    /**
812     * Send a command to the effect engine. This method is intended to send
813     * proprietary commands to a particular effect implementation.
814     * In case of success, returns the number of meaningful bytes in reply array.
815     * In case of failure, the returned value is negative and implementation specific.
816     * @hide
817     */
818    public int command(int cmdCode, byte[] command, byte[] reply)
819            throws IllegalStateException {
820        checkState("command()");
821        return native_command(cmdCode, command.length, command, reply.length, reply);
822    }
823
824    // --------------------------------------------------------------------------
825    // Getters
826    // --------------------
827
828    /**
829     * Returns effect unique identifier. This system wide unique identifier can
830     * be used to attach this effect to a MediaPlayer or an AudioTrack when the
831     * effect is an auxiliary effect (Reverb)
832     *
833     * @return the effect identifier.
834     * @throws IllegalStateException
835     */
836    public int getId() throws IllegalStateException {
837        checkState("getId()");
838        return mId;
839    }
840
841    /**
842     * Returns effect enabled state
843     *
844     * @return true if the effect is enabled, false otherwise.
845     * @throws IllegalStateException
846     */
847    public boolean getEnabled() throws IllegalStateException {
848        checkState("getEnabled()");
849        return native_getEnabled();
850    }
851
852    /**
853     * Checks if this AudioEffect object is controlling the effect engine.
854     *
855     * @return true if this instance has control of effect engine, false
856     *         otherwise.
857     * @throws IllegalStateException
858     */
859    public boolean hasControl() throws IllegalStateException {
860        checkState("hasControl()");
861        return native_hasControl();
862    }
863
864    // --------------------------------------------------------------------------
865    // Initialization / configuration
866    // --------------------
867    /**
868     * Sets the listener AudioEffect notifies when the effect engine is enabled
869     * or disabled.
870     *
871     * @param listener
872     */
873    public void setEnableStatusListener(OnEnableStatusChangeListener listener) {
874        synchronized (mListenerLock) {
875            mEnableStatusChangeListener = listener;
876        }
877        if ((listener != null) && (mNativeEventHandler == null)) {
878            createNativeEventHandler();
879        }
880    }
881
882    /**
883     * Sets the listener AudioEffect notifies when the effect engine control is
884     * taken or returned.
885     *
886     * @param listener
887     */
888    public void setControlStatusListener(OnControlStatusChangeListener listener) {
889        synchronized (mListenerLock) {
890            mControlChangeStatusListener = listener;
891        }
892        if ((listener != null) && (mNativeEventHandler == null)) {
893            createNativeEventHandler();
894        }
895    }
896
897    /**
898     * Sets the listener AudioEffect notifies when a parameter is changed.
899     *
900     * @param listener
901     * @hide
902     */
903    public void setParameterListener(OnParameterChangeListener listener) {
904        synchronized (mListenerLock) {
905            mParameterChangeListener = listener;
906        }
907        if ((listener != null) && (mNativeEventHandler == null)) {
908            createNativeEventHandler();
909        }
910    }
911
912    // Convenience method for the creation of the native event handler
913    // It is called only when a non-null event listener is set.
914    // precondition:
915    // mNativeEventHandler is null
916    private void createNativeEventHandler() {
917        Looper looper;
918        if ((looper = Looper.myLooper()) != null) {
919            mNativeEventHandler = new NativeEventHandler(this, looper);
920        } else if ((looper = Looper.getMainLooper()) != null) {
921            mNativeEventHandler = new NativeEventHandler(this, looper);
922        } else {
923            mNativeEventHandler = null;
924        }
925    }
926
927    // ---------------------------------------------------------
928    // Interface definitions
929    // --------------------
930    /**
931     * The OnEnableStatusChangeListener interface defines a method called by the AudioEffect
932     * when a the enabled state of the effect engine was changed by the controlling application.
933     */
934    public interface OnEnableStatusChangeListener {
935        /**
936         * Called on the listener to notify it that the effect engine has been
937         * enabled or disabled.
938         * @param effect the effect on which the interface is registered.
939         * @param enabled new effect state.
940         */
941        void onEnableStatusChange(AudioEffect effect, boolean enabled);
942    }
943
944    /**
945     * The OnControlStatusChangeListener interface defines a method called by the AudioEffect
946     * when a the control of the effect engine is gained or lost by the application
947     */
948    public interface OnControlStatusChangeListener {
949        /**
950         * Called on the listener to notify it that the effect engine control
951         * has been taken or returned.
952         * @param effect the effect on which the interface is registered.
953         * @param controlGranted true if the application has been granted control of the effect
954         * engine, false otherwise.
955         */
956        void onControlStatusChange(AudioEffect effect, boolean controlGranted);
957    }
958
959    /**
960     * The OnParameterChangeListener interface defines a method called by the AudioEffect
961     * when a parameter is changed in the effect engine by the controlling application.
962     * @hide
963     */
964    public interface OnParameterChangeListener {
965        /**
966         * Called on the listener to notify it that a parameter value has changed.
967         * @param effect the effect on which the interface is registered.
968         * @param status status of the set parameter operation.
969         * @param param ID of the modified parameter.
970         * @param value the new parameter value.
971         */
972        void onParameterChange(AudioEffect effect, int status, byte[] param,
973                byte[] value);
974    }
975
976
977    // -------------------------------------------------------------------------
978    // Audio Effect Control panel intents
979    // -------------------------------------------------------------------------
980
981    /**
982     *  Intent to launch an audio effect control panel UI.
983     *  <p>The goal of this intent is to enable separate implementations of music/media player
984     *  applications and audio effect control application or services.
985     *  This will allow platform vendors to offer more advanced control options for standard effects
986     *  or control for platform specific effects.
987     *  <p>The intent carries a number of extras used by the player application to communicate
988     *  necessary pieces of information to the control panel application.
989     *  <p>The calling application must use the
990     *  {@link android.app.Activity#startActivityForResult(Intent, int)} method to launch the
991     *  control panel so that its package name is indicated and used by the control panel
992     *  application to keep track of changes for this particular application.
993     *  <p>The {@link #EXTRA_AUDIO_SESSION} extra will indicate an audio session to which the
994     *  audio effects should be applied. If no audio session is specified, either one of the
995     *  follownig will happen:
996     *  <p>- If an audio session was previously opened by the calling application with
997     *  {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intent, the effect changes will
998     *  be applied to that session.
999     *  <p>- If no audio session is opened, the changes will be stored in the package specific
1000     *  storage area and applied whenever a new audio session is opened by this application.
1001     *  <p>The {@link #EXTRA_CONTENT_TYPE} extra will help the control panel application
1002     *  customize both the UI layout and the default audio effect settings if none are already
1003     *  stored for the calling application.
1004     */
1005    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
1006    public static final String ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL =
1007        "android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL";
1008
1009    /**
1010     *  Intent to signal to the effect control application or service that a new audio session
1011     *  is opened and requires audio effects to be applied.
1012     *  <p>This is different from {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} in that no
1013     *  UI should be displayed in this case. Music player applications can broadcast this intent
1014     *  before starting playback to make sure that any audio effect settings previously selected
1015     *  by the user are applied.
1016     *  <p>The effect control application receiving this intent will look for previously stored
1017     *  settings for the calling application, create all required audio effects and apply the
1018     *  effect settings to the specified audio session.
1019     *  <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the
1020     *  audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory.
1021     *  <p>If no stored settings are found for the calling application, default settings for the
1022     *  content type indicated by {@link #EXTRA_CONTENT_TYPE} will be applied. The default settings
1023     *  for a given content type are platform specific.
1024     */
1025    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
1026    public static final String ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION =
1027        "android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION";
1028
1029    /**
1030     *  Intent to signal to the effect control application or service that an audio session
1031     *  is closed and that effects should not be applied anymore.
1032     *  <p>The effect control application receiving this intent will delete all effects on
1033     *  this session and store current settings in package specific storage.
1034     *  <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the
1035     *  audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory.
1036     *  <p>It is good practice for applications to broadcast this intent when music playback stops
1037     *  and/or when exiting to free system resources consumed by audio effect engines.
1038     */
1039    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
1040    public static final String ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION =
1041        "android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION";
1042
1043    /**
1044     * Contains the ID of the audio session the effects should be applied to.
1045     * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL},
1046     * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
1047     * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
1048     * <p>The extra value is of type int and is the audio session ID.
1049     *  @see android.media.MediaPlayer#getAudioSessionId() for details on audio sessions.
1050     */
1051     public static final String EXTRA_AUDIO_SESSION = "android.media.extra.AUDIO_SESSION";
1052
1053    /**
1054     * Contains the package name of the calling application.
1055     * <p>This extra is for use with {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
1056     * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
1057     * <p>The extra value is a string containing the full package name.
1058     */
1059    public static final String EXTRA_PACKAGE_NAME = "android.media.extra.PACKAGE_NAME";
1060
1061    /**
1062     * Indicates which type of content is played by the application.
1063     * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} and
1064     * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intents.
1065     * <p>This information is used by the effect control application to customize UI and select
1066     * appropriate default effect settings. The content type is one of the following:
1067     * <ul>
1068     *   <li>{@link #CONTENT_TYPE_MUSIC}</li>
1069     *   <li>{@link #CONTENT_TYPE_MOVIE}</li>
1070     *   <li>{@link #CONTENT_TYPE_GAME}</li>
1071     *   <li>{@link #CONTENT_TYPE_VOICE}</li>
1072     * </ul>
1073     * If omitted, the content type defaults to {@link #CONTENT_TYPE_MUSIC}.
1074     */
1075    public static final String EXTRA_CONTENT_TYPE = "android.media.extra.CONTENT_TYPE";
1076
1077    /**
1078     * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is music
1079     */
1080    public static final int  CONTENT_TYPE_MUSIC = 0;
1081    /**
1082     * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is video or movie
1083     */
1084    public static final int  CONTENT_TYPE_MOVIE = 1;
1085    /**
1086     * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is game audio
1087     */
1088    public static final int  CONTENT_TYPE_GAME = 2;
1089    /**
1090     * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is voice audio
1091     */
1092    public static final int  CONTENT_TYPE_VOICE = 3;
1093
1094
1095    // ---------------------------------------------------------
1096    // Inner classes
1097    // --------------------
1098    /**
1099     * Helper class to handle the forwarding of native events to the appropriate
1100     * listeners
1101     */
1102    private class NativeEventHandler extends Handler {
1103        private AudioEffect mAudioEffect;
1104
1105        public NativeEventHandler(AudioEffect ae, Looper looper) {
1106            super(looper);
1107            mAudioEffect = ae;
1108        }
1109
1110        @Override
1111        public void handleMessage(Message msg) {
1112            if (mAudioEffect == null) {
1113                return;
1114            }
1115            switch (msg.what) {
1116            case NATIVE_EVENT_ENABLED_STATUS:
1117                OnEnableStatusChangeListener enableStatusChangeListener = null;
1118                synchronized (mListenerLock) {
1119                    enableStatusChangeListener = mAudioEffect.mEnableStatusChangeListener;
1120                }
1121                if (enableStatusChangeListener != null) {
1122                    enableStatusChangeListener.onEnableStatusChange(
1123                            mAudioEffect, (boolean) (msg.arg1 != 0));
1124                }
1125                break;
1126            case NATIVE_EVENT_CONTROL_STATUS:
1127                OnControlStatusChangeListener controlStatusChangeListener = null;
1128                synchronized (mListenerLock) {
1129                    controlStatusChangeListener = mAudioEffect.mControlChangeStatusListener;
1130                }
1131                if (controlStatusChangeListener != null) {
1132                    controlStatusChangeListener.onControlStatusChange(
1133                            mAudioEffect, (boolean) (msg.arg1 != 0));
1134                }
1135                break;
1136            case NATIVE_EVENT_PARAMETER_CHANGED:
1137                OnParameterChangeListener parameterChangeListener = null;
1138                synchronized (mListenerLock) {
1139                    parameterChangeListener = mAudioEffect.mParameterChangeListener;
1140                }
1141                if (parameterChangeListener != null) {
1142                    // arg1 contains offset of parameter value from start of
1143                    // byte array
1144                    int vOffset = msg.arg1;
1145                    byte[] p = (byte[]) msg.obj;
1146                    // See effect_param_t in EffectApi.h for psize and vsize
1147                    // fields offsets
1148                    int status = byteArrayToInt(p, 0);
1149                    int psize = byteArrayToInt(p, 4);
1150                    int vsize = byteArrayToInt(p, 8);
1151                    byte[] param = new byte[psize];
1152                    byte[] value = new byte[vsize];
1153                    System.arraycopy(p, 12, param, 0, psize);
1154                    System.arraycopy(p, vOffset, value, 0, vsize);
1155
1156                    parameterChangeListener.onParameterChange(mAudioEffect,
1157                            status, param, value);
1158                }
1159                break;
1160
1161            default:
1162                Log.e(TAG, "handleMessage() Unknown event type: " + msg.what);
1163                break;
1164            }
1165        }
1166    }
1167
1168    // ---------------------------------------------------------
1169    // Java methods called from the native side
1170    // --------------------
1171    @SuppressWarnings("unused")
1172    private static void postEventFromNative(Object effect_ref, int what,
1173            int arg1, int arg2, Object obj) {
1174        AudioEffect effect = (AudioEffect) ((WeakReference) effect_ref).get();
1175        if (effect == null) {
1176            return;
1177        }
1178        if (effect.mNativeEventHandler != null) {
1179            Message m = effect.mNativeEventHandler.obtainMessage(what, arg1,
1180                    arg2, obj);
1181            effect.mNativeEventHandler.sendMessage(m);
1182        }
1183
1184    }
1185
1186    // ---------------------------------------------------------
1187    // Native methods called from the Java side
1188    // --------------------
1189
1190    private static native final void native_init();
1191
1192    private native final int native_setup(Object audioeffect_this, String type,
1193            String uuid, int priority, int audioSession, int[] id, Object[] desc);
1194
1195    private native final void native_finalize();
1196
1197    private native final void native_release();
1198
1199    private native final int native_setEnabled(boolean enabled);
1200
1201    private native final boolean native_getEnabled();
1202
1203    private native final boolean native_hasControl();
1204
1205    private native final int native_setParameter(int psize, byte[] param,
1206            int vsize, byte[] value);
1207
1208    private native final int native_getParameter(int psize, byte[] param,
1209            int vsize, byte[] value);
1210
1211    private native final int native_command(int cmdCode, int cmdSize,
1212            byte[] cmdData, int repSize, byte[] repData);
1213
1214    private static native Object[] native_query_effects();
1215
1216    private static native Object[] native_query_pre_processing(int audioSession);
1217
1218    // ---------------------------------------------------------
1219    // Utility methods
1220    // ------------------
1221
1222    /**
1223    * @hide
1224    */
1225    public void checkState(String methodName) throws IllegalStateException {
1226        synchronized (mStateLock) {
1227            if (mState != STATE_INITIALIZED) {
1228                throw (new IllegalStateException(methodName
1229                        + " called on uninitialized AudioEffect."));
1230            }
1231        }
1232    }
1233
1234    /**
1235     * @hide
1236     */
1237    public void checkStatus(int status) {
1238        if (isError(status)) {
1239            switch (status) {
1240            case AudioEffect.ERROR_BAD_VALUE:
1241                throw (new IllegalArgumentException(
1242                        "AudioEffect: bad parameter value"));
1243            case AudioEffect.ERROR_INVALID_OPERATION:
1244                throw (new UnsupportedOperationException(
1245                        "AudioEffect: invalid parameter operation"));
1246            default:
1247                throw (new RuntimeException("AudioEffect: set/get parameter error"));
1248            }
1249        }
1250    }
1251
1252    /**
1253     * @hide
1254     */
1255    public static boolean isError(int status) {
1256        return (status < 0);
1257    }
1258
1259    /**
1260     * @hide
1261     */
1262    public int byteArrayToInt(byte[] valueBuf) {
1263        return byteArrayToInt(valueBuf, 0);
1264
1265    }
1266
1267    /**
1268     * @hide
1269     */
1270    public int byteArrayToInt(byte[] valueBuf, int offset) {
1271        ByteBuffer converter = ByteBuffer.wrap(valueBuf);
1272        converter.order(ByteOrder.nativeOrder());
1273        return converter.getInt(offset);
1274
1275    }
1276
1277    /**
1278     * @hide
1279     */
1280    public byte[] intToByteArray(int value) {
1281        ByteBuffer converter = ByteBuffer.allocate(4);
1282        converter.order(ByteOrder.nativeOrder());
1283        converter.putInt(value);
1284        return converter.array();
1285    }
1286
1287    /**
1288     * @hide
1289     */
1290    public short byteArrayToShort(byte[] valueBuf) {
1291        return byteArrayToShort(valueBuf, 0);
1292    }
1293
1294    /**
1295     * @hide
1296     */
1297    public short byteArrayToShort(byte[] valueBuf, int offset) {
1298        ByteBuffer converter = ByteBuffer.wrap(valueBuf);
1299        converter.order(ByteOrder.nativeOrder());
1300        return converter.getShort(offset);
1301
1302    }
1303
1304    /**
1305     * @hide
1306     */
1307    public byte[] shortToByteArray(short value) {
1308        ByteBuffer converter = ByteBuffer.allocate(2);
1309        converter.order(ByteOrder.nativeOrder());
1310        short sValue = (short) value;
1311        converter.putShort(sValue);
1312        return converter.array();
1313    }
1314
1315    /**
1316     * @hide
1317     */
1318    public byte[] concatArrays(byte[]... arrays) {
1319        int len = 0;
1320        for (byte[] a : arrays) {
1321            len += a.length;
1322        }
1323        byte[] b = new byte[len];
1324
1325        int offs = 0;
1326        for (byte[] a : arrays) {
1327            System.arraycopy(a, 0, b, offs, a.length);
1328            offs += a.length;
1329        }
1330        return b;
1331    }
1332}
1333