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