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