Virtualizer.java revision d2bebb3ab86177c0d27664af86b30b7dce2c9bcb
1fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent/*
217cb280e7f1ac3556eac90ab08263712e0348cb9Eric Laurent * Copyright (C) 2010 The Android Open Source Project
3fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent *
4fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * Licensed under the Apache License, Version 2.0 (the "License");
5fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * you may not use this file except in compliance with the License.
6fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * You may obtain a copy of the License at
7fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent *
8fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent *      http://www.apache.org/licenses/LICENSE-2.0
9fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent *
10fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * Unless required by applicable law or agreed to in writing, software
11fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * distributed under the License is distributed on an "AS IS" BASIS,
12fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * See the License for the specific language governing permissions and
14fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * limitations under the License.
15fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent */
16fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
171a5149e5d7f2dddc8b324f7695e69fd89af73c52Eric Laurentpackage android.media.audiofx;
18fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
19d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Triviimport android.media.AudioDevice;
20d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Triviimport android.media.AudioFormat;
211a5149e5d7f2dddc8b324f7695e69fd89af73c52Eric Laurentimport android.media.audiofx.AudioEffect;
22fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurentimport android.util.Log;
23ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
24d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Triviimport java.nio.ByteBuffer;
25d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Triviimport java.nio.ByteOrder;
26ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurentimport java.util.StringTokenizer;
27fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
28fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
29fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent/**
30fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * An audio virtualizer is a general name for an effect to spatialize audio channels. The exact
31fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * behavior of this effect is dependent on the number of audio input channels and the types and
32fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * number of audio output channels of the device. For example, in the case of a stereo input and
33fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * stereo headphone output, a stereo widening effect is used when this effect is turned on.
34fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * <p>An application creates a Virtualizer object to instantiate and control a virtualizer engine
35fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * in the audio framework.
36fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * <p>The methods, parameter types and units exposed by the Virtualizer implementation are directly
37fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/)
38fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * for the SLVirtualizerItf interface. Please refer to this specification for more details.
39fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * <p>To attach the Virtualizer to a particular AudioTrack or MediaPlayer, specify the audio session
4062f3617f2f4016ad2f59635d5156d64872989880Eric Laurent * ID of this AudioTrack or MediaPlayer when constructing the Virtualizer.
4162f3617f2f4016ad2f59635d5156d64872989880Eric Laurent * <p>NOTE: attaching a Virtualizer to the global audio output mix by use of session 0 is
4262f3617f2f4016ad2f59635d5156d64872989880Eric Laurent * deprecated.
431a5149e5d7f2dddc8b324f7695e69fd89af73c52Eric Laurent * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
441a5149e5d7f2dddc8b324f7695e69fd89af73c52Eric Laurent * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
451a5149e5d7f2dddc8b324f7695e69fd89af73c52Eric Laurent * audio effects.
46fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent */
47fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
48fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurentpublic class Virtualizer extends AudioEffect {
49fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
50fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    private final static String TAG = "Virtualizer";
51d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    private final static boolean DEBUG = false;
52fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
53d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    // These constants must be synchronized with those in
54d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    //        system/media/audio_effects/include/audio_effects/effect_virtualizer.h
55fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
56fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Is strength parameter supported by virtualizer engine. Parameter ID for getParameter().
57fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
58fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public static final int PARAM_STRENGTH_SUPPORTED = 0;
59fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
60fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Virtualizer effect strength. Parameter ID for
611a5149e5d7f2dddc8b324f7695e69fd89af73c52Eric Laurent     * {@link android.media.audiofx.Virtualizer.OnParameterChangeListener}
62fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
63fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public static final int PARAM_STRENGTH = 1;
64d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
65d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @hide
66d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Parameter ID to query the virtual speaker angles for a channel mask / device configuration.
67d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
68d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    public static final int PARAM_VIRTUAL_SPEAKER_ANGLES = 2;
69d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
70d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @hide
71d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Parameter ID to force the virtualization mode to be that of a specific device
72d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
73d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    public static final int PARAM_FORCE_VIRTUALIZATION_MODE = 3;
74d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
75d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @hide
76d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Parameter ID to query the current virtualization mode.
77d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
78d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    public static final int PARAM_VIRTUALIZATION_MODE = 4;
79fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
80fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
81fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Indicates if strength parameter is supported by the virtualizer engine
82fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
83fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    private boolean mStrengthSupported = false;
84fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
85fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
86fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Registered listener for parameter changes.
87fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
88fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    private OnParameterChangeListener mParamListener = null;
89fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
90fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
91fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Listener used internally to to receive raw parameter change event from AudioEffect super class
92fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
93fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    private BaseParameterListener mBaseParamListener = null;
94fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
95fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
96fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Lock for access to mParamListener
97fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
98fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    private final Object mParamListenerLock = new Object();
99fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
100fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
101fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Class constructor.
102fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @param priority the priority level requested by the application for controlling the Virtualizer
103fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * engine. As the same engine can be shared by several applications, this parameter indicates
104fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * how much the requesting application needs control of effect parameters. The normal priority
105fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * is 0, above normal is a positive number, below normal a negative number.
10662f3617f2f4016ad2f59635d5156d64872989880Eric Laurent     * @param audioSession  system wide unique audio session identifier. The Virtualizer will
10762f3617f2f4016ad2f59635d5156d64872989880Eric Laurent     * be attached to the MediaPlayer or AudioTrack in the same audio session.
108fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     *
109fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws java.lang.IllegalStateException
110fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws java.lang.IllegalArgumentException
111fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws java.lang.UnsupportedOperationException
112fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws java.lang.RuntimeException
113fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
114fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public Virtualizer(int priority, int audioSession)
115fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    throws IllegalStateException, IllegalArgumentException,
116fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent           UnsupportedOperationException, RuntimeException {
117fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        super(EFFECT_TYPE_VIRTUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
118fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
11962f3617f2f4016ad2f59635d5156d64872989880Eric Laurent        if (audioSession == 0) {
12062f3617f2f4016ad2f59635d5156d64872989880Eric Laurent            Log.w(TAG, "WARNING: attaching a Virtualizer to global output mix is deprecated!");
12162f3617f2f4016ad2f59635d5156d64872989880Eric Laurent        }
12262f3617f2f4016ad2f59635d5156d64872989880Eric Laurent
123ba8da2e61b1d9ebb7a4758f1f7849ff8440bd20cEric Laurent        int[] value = new int[1];
124fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
125fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        mStrengthSupported = (value[0] != 0);
126fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
127fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
128fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
129fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Indicates whether setting strength is supported. If this method returns false, only one
130fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * strength is supported and the setStrength() method always rounds to that value.
131fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @return true is strength parameter is supported, false otherwise
132fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
133fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public boolean getStrengthSupported() {
134fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent       return mStrengthSupported;
135fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
136fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
137fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
138fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Sets the strength of the virtualizer effect. If the implementation does not support per mille
139fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * accuracy for setting the strength, it is allowed to round the given strength to the nearest
140fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * supported value. You can use the {@link #getRoundedStrength()} method to query the
141fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * (possibly rounded) value that was actually set.
14217cb280e7f1ac3556eac90ab08263712e0348cb9Eric Laurent     * @param strength strength of the effect. The valid range for strength strength is [0, 1000],
143fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * where 0 per mille designates the mildest effect and 1000 per mille designates the strongest.
144fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws IllegalStateException
145fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws IllegalArgumentException
146fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws UnsupportedOperationException
147fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
148fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public void setStrength(short strength)
149fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
150fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        checkStatus(setParameter(PARAM_STRENGTH, strength));
151fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
152fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
153fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
154fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Gets the current strength of the effect.
15517cb280e7f1ac3556eac90ab08263712e0348cb9Eric Laurent     * @return the strength of the effect. The valid range for strength is [0, 1000], where 0 per
156fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * mille designates the mildest effect and 1000 per mille the strongest
157fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws IllegalStateException
158fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws IllegalArgumentException
159fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws UnsupportedOperationException
160fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
161fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public short getRoundedStrength()
162fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
163fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        short[] value = new short[1];
164fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        checkStatus(getParameter(PARAM_STRENGTH, value));
165fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        return value[0];
166fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
167fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
168fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
169d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Checks if a configuration is supported, and query the virtual speaker angles.
170d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param inputChannelMask
171d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param deviceType
172d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param angles if non-null: array in which the angles will be written. If null, no angles
173d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    are returned
174d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @return true if the combination of channel mask and output device type is supported, false
175d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    otherwise
176d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalStateException
177d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalArgumentException
178d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws UnsupportedOperationException
179d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
180d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    private boolean getAnglesInt(int inputChannelMask, int deviceType, int[] angles)
181d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
182d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // parameter check
183d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if (inputChannelMask == AudioFormat.CHANNEL_INVALID) {
184d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throw (new IllegalArgumentException(
185d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    "Virtualizer: illegal CHANNEL_INVALID channel mask"));
186d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
187d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        int channelMask = inputChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT ?
188d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                AudioFormat.CHANNEL_OUT_STEREO : inputChannelMask;
189d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        int nbChannels = AudioFormat.channelCountFromOutChannelMask(channelMask);
190d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if ((angles != null) && (angles.length < (nbChannels * 3))) {
191d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            Log.e(TAG, "Size of array for angles cannot accomodate number of channels in mask ("
192d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    + nbChannels + ")");
193d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throw (new IllegalArgumentException(
194d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    "Virtualizer: array for channel / angle pairs is too small: is " + angles.length
195d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    + ", should be " + (nbChannels * 3)));
196d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
197d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
198d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        ByteBuffer paramsConverter = ByteBuffer.allocate(3 /* param + mask + device*/ * 4);
199d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        paramsConverter.order(ByteOrder.nativeOrder());
200d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        paramsConverter.putInt(PARAM_VIRTUAL_SPEAKER_ANGLES);
201d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // convert channel mask to internal native representation
202d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        paramsConverter.putInt(AudioFormat.convertChannelOutMaskToNativeMask(channelMask));
203d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // convert Java device type to internal representation
204d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        paramsConverter.putInt(AudioDevice.convertDeviceTypeToInternalDevice(deviceType));
205d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // allocate an array to store the results
206d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        byte[] result = new byte[nbChannels * 4/*int to byte*/ * 3/*for mask, azimuth, elevation*/];
207d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
208d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // call into the effect framework
209d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        int status = getParameter(paramsConverter.array(), result);
210d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if (DEBUG) {
211d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            Log.v(TAG, "getAngles(0x" + Integer.toHexString(inputChannelMask) + ", 0x"
212d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    + Integer.toHexString(deviceType) + ") returns " + status);
213d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
214d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
215d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if (status >= 0) {
216d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            if (angles != null) {
217d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                // convert and copy the results
218d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                ByteBuffer resultConverter = ByteBuffer.wrap(result);
219d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                resultConverter.order(ByteOrder.nativeOrder());
220d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                for (int i = 0 ; i < nbChannels ; i++) {
221d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    // write the channel mask
222d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    angles[3 * i] = AudioFormat.convertNativeChannelMaskToOutMask(
223d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                            resultConverter.getInt((i * 4 * 3)));
224d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    // write the azimuth
225d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    angles[3 * i + 1] = resultConverter.getInt(i * 4 * 3 + 4);
226d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    // write the elevation
227d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    angles[3 * i + 2] = resultConverter.getInt(i * 4 * 3 + 8);
228d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    if (DEBUG) {
229d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                        Log.v(TAG, "channel 0x" + Integer.toHexString(angles[3*i]).toUpperCase()
230d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                                + " at az=" + angles[3*i+1] + "deg"
231d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                                + " elev="  + angles[3*i+2] + "deg");
232d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    }
233d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                }
234d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            }
235d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            return true;
236d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        } else if (status == AudioEffect.ERROR_BAD_VALUE) {
237d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            // a BAD_VALUE return from getParameter indicates the configuration is not supported
238d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            // don't throw an exception, just return false
239d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            return false;
240d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        } else {
241d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            // something wrong may have happened
242d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            checkStatus(status);
243d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
244d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // unexpected virtualizer behavior
245d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        Log.e(TAG, "unexpected status code " + status
246d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                + " after getParameter(PARAM_VIRTUAL_SPEAKER_ANGLES)");
247d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        return false;
248d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    }
249d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
250d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
251d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @hide
252d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * CANDIDATE FOR PUBLIC API
253d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Checks if the combination of a channel mask and device type is supported by this virtualizer.
254d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Some virtualizer implementations may only support binaural processing (i.e. only support
255d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * headphone output), some may support transaural processing (i.e. for speaker output) for the
256d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * built-in speakers. Use this method to query the virtualizer implementation capabilities.
257d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param inputChannelMask the channel mask of the content to virtualize.
258d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param deviceType the device type for which virtualization processing is to be performed.
259d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    Valid values are the device types defined in {@link AudioDevice}.
260d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @return true if the combination of channel mask and output device type is supported, false
261d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    otherwise.
262d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    <br>An indication that a certain channel mask is not supported doesn't necessarily mean
263d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    you cannot play content with that channel mask, it more likely implies the content will
264d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    be downmixed before being virtualized. For instance a virtualizer that only supports a
265d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    mask such as {@link AudioFormat#CHANNEL_OUT_STEREO}
266d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    will still be able to process content with a mask of
267d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    {@link AudioFormat#CHANNEL_OUT_5POINT1}, but will downmix the content to stereo first, and
268d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    then will virtualize, as opposed to virtualizing each channel individually.
269d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalStateException
270d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalArgumentException
271d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws UnsupportedOperationException
272d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
273d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    public boolean canVirtualize(int inputChannelMask, int deviceType)
274d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
275d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        return getAnglesInt(inputChannelMask, deviceType, null);
276d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    }
277d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
278d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
279d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @hide
280d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * CANDIDATE FOR PUBLIC API
281d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Queries the virtual speaker angles (azimuth and elevation) for a combination of a channel
282d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * mask and device type.
283d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * If the virtualization configuration (mask and device) is supported (see
284d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * {@link #canVirtualize(int, int)}, the array angles will contain upon return the
285d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * definition of each virtual speaker and its azimuth and elevation angles relative to the
286d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * listener.
287d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * <br>Note that in some virtualizer implementations, the angles may be strength-dependent.
288d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param inputChannelMask the channel mask of the content to virtualize.
289d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param deviceType the device type for which virtualization processing is to be performed.
290d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    Valid values are the device types defined in {@link AudioDevice}.
291d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param angles a non-null array whose length is 3 times the number of channels in the channel
292d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    mask.
293d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    If the method indicates the configuration is supported, the array will contain upon return
294d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    triplets of values: for each channel <code>i</code> among the channels of the mask:
295d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    <ul>
296d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *      <li>the element at index <code>3*i</code> in the array contains the speaker
297d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *          identification (e.g. {@link AudioFormat#CHANNEL_OUT_FRONT_LEFT}),</li>
298d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *      <li>the element at index <code>3*i+1</code> contains its corresponding azimuth angle
299d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *          expressed in degrees, where 0 is the direction the listener faces, 180 is behind
300d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *          the listener, and -90 is to her/his left,</li>
301d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *      <li>the element at index <code>3*i+2</code> contains its corresponding elevation angle
302d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *          where +90 is directly above the listener, 0 is the horizontal plane, and -90 is
303d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *          directly below the listener.</li>
304d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @return true if the combination of channel mask and output device type is supported, false
305d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    otherwise.
306d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalStateException
307d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalArgumentException
308d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws UnsupportedOperationException
309d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
310d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    public boolean getSpeakerAngles(int inputChannelMask, int deviceType, int[] angles)
311d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
312d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if (angles == null) {
313d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throw (new IllegalArgumentException(
314d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    "Virtualizer: illegal null channel / angle array"));
315d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
316d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
317d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        return getAnglesInt(inputChannelMask, deviceType, angles);
318d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    }
319d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
320d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
321d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @hide
322d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * CANDIDATE FOR PUBLIC API
323d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Forces the virtualizer effect to use the processing mode used for the given device type.
324d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * The effect must be enabled for the forced mode to be applied.
325d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param deviceType one of the device types defined in {@link AudioDevice}.
326d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *     Use {@link AudioDevice#DEVICE_TYPE_UNKNOWN} to return to the non-forced mode.
327d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @return true if the processing mode for the device type is supported, and it is successfully
328d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *     set, or forcing was successfully disabled with {@link AudioDevice#DEVICE_TYPE_UNKNOWN},
329d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *     false otherwise.
330d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalStateException
331d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalArgumentException
332d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws UnsupportedOperationException
333d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
334d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    public boolean forceVirtualizationMode(int deviceType)
335d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
336d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // convert Java device type to internal representation
337d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        int internalDevice = AudioDevice.convertDeviceTypeToInternalDevice(deviceType);
338d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
339d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        int status = setParameter(PARAM_FORCE_VIRTUALIZATION_MODE, internalDevice);
340d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
341d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if (status >= 0) {
342d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            return true;
343d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        } else if (status == AudioEffect.ERROR_BAD_VALUE) {
344d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            // a BAD_VALUE return from setParameter indicates the mode can't be forced to that
345d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            // of this device, don't throw an exception, just return false
346d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            return false;
347d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        } else {
348d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            // something wrong may have happened
349d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            checkStatus(status);
350d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
351d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // unexpected virtualizer behavior
352d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        Log.e(TAG, "unexpected status code " + status
353d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                + " after setParameter(PARAM_FORCE_VIRTUALIZATION_MODE)");
354d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        return false;
355d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    }
356d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
357d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
358d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @hide
359d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * CANDIDATE FOR PUBLIC API
360d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Return the device type which reflects the virtualization mode being used, if any.
361d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @return a device type (as defined in {@link AudioDevice}) which reflects the virtualization
362d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *     mode being used.
363d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *     If virtualization is not active, the device type will be
364d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *     {@link AudioDevice#DEVICE_TYPE_UNKNOWN}. Virtualization may not be active either because
365d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *     the effect is not enabled or because the current output device is not compatible with
366d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *     this virtualization implementation.
367d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
368d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    public int getVirtualizationMode() {
369d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        int[] value = new int[1];
370d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        int status = getParameter(PARAM_VIRTUALIZATION_MODE, value);
371d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if (status >= 0) {
372d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            return AudioDevice.convertInternalDeviceToDeviceType(value[0]);
373d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        } else if (status == AudioEffect.ERROR_BAD_VALUE) {
374d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            return AudioDevice.DEVICE_TYPE_UNKNOWN;
375d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        } else {
376d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            // something wrong may have happened
377d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            checkStatus(status);
378d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
379d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // unexpected virtualizer behavior
380d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        Log.e(TAG, "unexpected status code " + status
381d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                + " after getParameter(PARAM_VIRTUALIZATION_MODE)");
382d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        return AudioDevice.DEVICE_TYPE_UNKNOWN;
383d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    }
384d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
385d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
386fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * The OnParameterChangeListener interface defines a method called by the Virtualizer when a
387fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * parameter value has changed.
388fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
389fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public interface OnParameterChangeListener  {
390fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        /**
391fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * Method called when a parameter value has changed. The method is called only if the
392fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * parameter was changed by another application having the control of the same
393fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * Virtualizer engine.
394fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * @param effect the Virtualizer on which the interface is registered.
395fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * @param status status of the set parameter operation.
396fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ...
397fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * @param value the new parameter value.
398fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         */
399fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        void onParameterChange(Virtualizer effect, int status, int param, short value);
400fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
401fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
402fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
403fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Listener used internally to receive unformatted parameter change events from AudioEffect
404fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * super class.
405fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
406fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
407fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        private BaseParameterListener() {
408fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
409fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        }
410fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
411fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            OnParameterChangeListener l = null;
412fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
413fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            synchronized (mParamListenerLock) {
414fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                if (mParamListener != null) {
415fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                    l = mParamListener;
416fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                }
417fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            }
418fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            if (l != null) {
419fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                int p = -1;
420fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                short v = -1;
421fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
422fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                if (param.length == 4) {
423fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                    p = byteArrayToInt(param, 0);
424fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                }
425fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                if (value.length == 2) {
426fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                    v = byteArrayToShort(value, 0);
427fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                }
428fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                if (p != -1 && v != -1) {
429fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                    l.onParameterChange(Virtualizer.this, status, p, v);
430fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                }
431fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            }
432fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        }
433fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
434fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
435fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
436fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Registers an OnParameterChangeListener interface.
437fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @param listener OnParameterChangeListener interface registered
438fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
439fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public void setParameterListener(OnParameterChangeListener listener) {
440fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        synchronized (mParamListenerLock) {
441fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            if (mParamListener == null) {
442fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                mParamListener = listener;
443fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                mBaseParamListener = new BaseParameterListener();
444fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                super.setParameterListener(mBaseParamListener);
445fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            }
446fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        }
447fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
448ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
449ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    /**
450ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * The Settings class regroups all virtualizer parameters. It is used in
451ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * conjuntion with getProperties() and setProperties() methods to backup and restore
452ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * all parameters in a single call.
453ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     */
454ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    public static class Settings {
455ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        public short strength;
456ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
457ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        public Settings() {
458ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        }
459ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
460ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        /**
461ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent         * Settings class constructor from a key=value; pairs formatted string. The string is
462ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent         * typically returned by Settings.toString() method.
463ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent         * @throws IllegalArgumentException if the string is not correctly formatted.
464ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent         */
465ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        public Settings(String settings) {
466ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            StringTokenizer st = new StringTokenizer(settings, "=;");
467ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            int tokens = st.countTokens();
468ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            if (st.countTokens() != 3) {
469ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                throw new IllegalArgumentException("settings: " + settings);
470ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            }
471ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            String key = st.nextToken();
472ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            if (!key.equals("Virtualizer")) {
473ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                throw new IllegalArgumentException(
474ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                        "invalid settings for Virtualizer: " + key);
475ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            }
476ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            try {
477ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                key = st.nextToken();
478ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                if (!key.equals("strength")) {
479ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                    throw new IllegalArgumentException("invalid key name: " + key);
480ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                }
481ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                strength = Short.parseShort(st.nextToken());
482ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent             } catch (NumberFormatException nfe) {
483ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                throw new IllegalArgumentException("invalid value for key: " + key);
484ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            }
485ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        }
486ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
487ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        @Override
488ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        public String toString() {
489ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            String str = new String (
490ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                    "Virtualizer"+
491ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                    ";strength="+Short.toString(strength)
492ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                    );
493ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            return str;
494ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        }
495ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    };
496ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
497ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
498ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    /**
499ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * Gets the virtualizer properties. This method is useful when a snapshot of current
500ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * virtualizer settings must be saved by the application.
501ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @return a Virtualizer.Settings object containing all current parameters values
502ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @throws IllegalStateException
503ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @throws IllegalArgumentException
504ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @throws UnsupportedOperationException
505ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     */
506ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    public Virtualizer.Settings getProperties()
507ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
508ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        Settings settings = new Settings();
509ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        short[] value = new short[1];
510ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        checkStatus(getParameter(PARAM_STRENGTH, value));
511ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        settings.strength = value[0];
512ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        return settings;
513ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    }
514ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
515ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    /**
516ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * Sets the virtualizer properties. This method is useful when virtualizer settings have to
517ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * be applied from a previous backup.
51817cb280e7f1ac3556eac90ab08263712e0348cb9Eric Laurent     * @param settings a Virtualizer.Settings object containing the properties to apply
519ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @throws IllegalStateException
520ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @throws IllegalArgumentException
521ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @throws UnsupportedOperationException
522ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     */
523ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    public void setProperties(Virtualizer.Settings settings)
524ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
525ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        checkStatus(setParameter(PARAM_STRENGTH, settings.strength));
526ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    }
527fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent}
528