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
19b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Triviimport android.annotation.IntDef;
20a33be211e768746745a0deeba71f8c6b65e72442Paul McLeanimport android.media.AudioDeviceInfo;
21d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Triviimport android.media.AudioFormat;
221a5149e5d7f2dddc8b324f7695e69fd89af73c52Eric Laurentimport android.media.audiofx.AudioEffect;
23fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurentimport android.util.Log;
24ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
25b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Triviimport java.lang.annotation.Retention;
26b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Triviimport java.lang.annotation.RetentionPolicy;
27d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Triviimport java.nio.ByteBuffer;
28d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Triviimport java.nio.ByteOrder;
29ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurentimport java.util.StringTokenizer;
30fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
31fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
32fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent/**
33fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * An audio virtualizer is a general name for an effect to spatialize audio channels. The exact
34fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * behavior of this effect is dependent on the number of audio input channels and the types and
35fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * number of audio output channels of the device. For example, in the case of a stereo input and
36fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * stereo headphone output, a stereo widening effect is used when this effect is turned on.
37fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * <p>An application creates a Virtualizer object to instantiate and control a virtualizer engine
38fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * in the audio framework.
39fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * <p>The methods, parameter types and units exposed by the Virtualizer implementation are directly
40fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/)
41fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * for the SLVirtualizerItf interface. Please refer to this specification for more details.
42fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent * <p>To attach the Virtualizer to a particular AudioTrack or MediaPlayer, specify the audio session
4362f3617f2f4016ad2f59635d5156d64872989880Eric Laurent * ID of this AudioTrack or MediaPlayer when constructing the Virtualizer.
4462f3617f2f4016ad2f59635d5156d64872989880Eric Laurent * <p>NOTE: attaching a Virtualizer to the global audio output mix by use of session 0 is
4562f3617f2f4016ad2f59635d5156d64872989880Eric Laurent * deprecated.
461a5149e5d7f2dddc8b324f7695e69fd89af73c52Eric Laurent * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
471a5149e5d7f2dddc8b324f7695e69fd89af73c52Eric Laurent * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
481a5149e5d7f2dddc8b324f7695e69fd89af73c52Eric Laurent * audio effects.
49fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent */
50fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
51fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurentpublic class Virtualizer extends AudioEffect {
52fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
53fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    private final static String TAG = "Virtualizer";
54d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    private final static boolean DEBUG = false;
55fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
56d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    // These constants must be synchronized with those in
57d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    //        system/media/audio_effects/include/audio_effects/effect_virtualizer.h
58fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
59fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Is strength parameter supported by virtualizer engine. Parameter ID for getParameter().
60fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
61fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public static final int PARAM_STRENGTH_SUPPORTED = 0;
62fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
63fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Virtualizer effect strength. Parameter ID for
641a5149e5d7f2dddc8b324f7695e69fd89af73c52Eric Laurent     * {@link android.media.audiofx.Virtualizer.OnParameterChangeListener}
65fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
66fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public static final int PARAM_STRENGTH = 1;
67d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
68d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @hide
69d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Parameter ID to query the virtual speaker angles for a channel mask / device configuration.
70d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
71d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    public static final int PARAM_VIRTUAL_SPEAKER_ANGLES = 2;
72d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
73d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @hide
74d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Parameter ID to force the virtualization mode to be that of a specific device
75d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
76d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    public static final int PARAM_FORCE_VIRTUALIZATION_MODE = 3;
77d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
78d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @hide
79d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Parameter ID to query the current virtualization mode.
80d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
81d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    public static final int PARAM_VIRTUALIZATION_MODE = 4;
82fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
83fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
84fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Indicates if strength parameter is supported by the virtualizer engine
85fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
86fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    private boolean mStrengthSupported = false;
87fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
88fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
89fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Registered listener for parameter changes.
90fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
91fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    private OnParameterChangeListener mParamListener = null;
92fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
93fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
94fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Listener used internally to to receive raw parameter change event from AudioEffect super class
95fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
96fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    private BaseParameterListener mBaseParamListener = null;
97fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
98fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
99fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Lock for access to mParamListener
100fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
101fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    private final Object mParamListenerLock = new Object();
102fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
103fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
104fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Class constructor.
105fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @param priority the priority level requested by the application for controlling the Virtualizer
106fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * engine. As the same engine can be shared by several applications, this parameter indicates
107fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * how much the requesting application needs control of effect parameters. The normal priority
108fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * is 0, above normal is a positive number, below normal a negative number.
10962f3617f2f4016ad2f59635d5156d64872989880Eric Laurent     * @param audioSession  system wide unique audio session identifier. The Virtualizer will
11062f3617f2f4016ad2f59635d5156d64872989880Eric Laurent     * be attached to the MediaPlayer or AudioTrack in the same audio session.
111fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     *
112fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws java.lang.IllegalStateException
113fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws java.lang.IllegalArgumentException
114fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws java.lang.UnsupportedOperationException
115fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws java.lang.RuntimeException
116fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
117fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public Virtualizer(int priority, int audioSession)
118fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    throws IllegalStateException, IllegalArgumentException,
119fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent           UnsupportedOperationException, RuntimeException {
120fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        super(EFFECT_TYPE_VIRTUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
121fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
12262f3617f2f4016ad2f59635d5156d64872989880Eric Laurent        if (audioSession == 0) {
12362f3617f2f4016ad2f59635d5156d64872989880Eric Laurent            Log.w(TAG, "WARNING: attaching a Virtualizer to global output mix is deprecated!");
12462f3617f2f4016ad2f59635d5156d64872989880Eric Laurent        }
12562f3617f2f4016ad2f59635d5156d64872989880Eric Laurent
126ba8da2e61b1d9ebb7a4758f1f7849ff8440bd20cEric Laurent        int[] value = new int[1];
127fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
128fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        mStrengthSupported = (value[0] != 0);
129fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
130fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
131fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
132fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Indicates whether setting strength is supported. If this method returns false, only one
133fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * strength is supported and the setStrength() method always rounds to that value.
134fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @return true is strength parameter is supported, false otherwise
135fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
136fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public boolean getStrengthSupported() {
137fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent       return mStrengthSupported;
138fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
139fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
140fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
141fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Sets the strength of the virtualizer effect. If the implementation does not support per mille
142fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * accuracy for setting the strength, it is allowed to round the given strength to the nearest
143fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * supported value. You can use the {@link #getRoundedStrength()} method to query the
144fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * (possibly rounded) value that was actually set.
14517cb280e7f1ac3556eac90ab08263712e0348cb9Eric Laurent     * @param strength strength of the effect. The valid range for strength strength is [0, 1000],
146fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * where 0 per mille designates the mildest effect and 1000 per mille designates the strongest.
147fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws IllegalStateException
148fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws IllegalArgumentException
149fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws UnsupportedOperationException
150fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
151fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public void setStrength(short strength)
152fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
153fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        checkStatus(setParameter(PARAM_STRENGTH, strength));
154fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
155fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
156fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
157fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Gets the current strength of the effect.
15817cb280e7f1ac3556eac90ab08263712e0348cb9Eric Laurent     * @return the strength of the effect. The valid range for strength is [0, 1000], where 0 per
159fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * mille designates the mildest effect and 1000 per mille the strongest
160fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws IllegalStateException
161fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws IllegalArgumentException
162fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @throws UnsupportedOperationException
163fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
164fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public short getRoundedStrength()
165fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
166fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        short[] value = new short[1];
167fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        checkStatus(getParameter(PARAM_STRENGTH, value));
168fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        return value[0];
169fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
170fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
171fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
172d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Checks if a configuration is supported, and query the virtual speaker angles.
173d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param inputChannelMask
174d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param deviceType
175d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param angles if non-null: array in which the angles will be written. If null, no angles
176d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    are returned
177d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @return true if the combination of channel mask and output device type is supported, false
178d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    otherwise
179d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalStateException
180d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalArgumentException
181d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws UnsupportedOperationException
182d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
183d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    private boolean getAnglesInt(int inputChannelMask, int deviceType, int[] angles)
184d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
185d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // parameter check
186d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if (inputChannelMask == AudioFormat.CHANNEL_INVALID) {
187d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throw (new IllegalArgumentException(
188d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    "Virtualizer: illegal CHANNEL_INVALID channel mask"));
189d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
190d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        int channelMask = inputChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT ?
191d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                AudioFormat.CHANNEL_OUT_STEREO : inputChannelMask;
192d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        int nbChannels = AudioFormat.channelCountFromOutChannelMask(channelMask);
193d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if ((angles != null) && (angles.length < (nbChannels * 3))) {
194d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            Log.e(TAG, "Size of array for angles cannot accomodate number of channels in mask ("
195d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    + nbChannels + ")");
196d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throw (new IllegalArgumentException(
197d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    "Virtualizer: array for channel / angle pairs is too small: is " + angles.length
198d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    + ", should be " + (nbChannels * 3)));
199d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
200d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
201d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        ByteBuffer paramsConverter = ByteBuffer.allocate(3 /* param + mask + device*/ * 4);
202d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        paramsConverter.order(ByteOrder.nativeOrder());
203d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        paramsConverter.putInt(PARAM_VIRTUAL_SPEAKER_ANGLES);
204d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // convert channel mask to internal native representation
205d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        paramsConverter.putInt(AudioFormat.convertChannelOutMaskToNativeMask(channelMask));
206d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // convert Java device type to internal representation
207a33be211e768746745a0deeba71f8c6b65e72442Paul McLean        paramsConverter.putInt(AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType));
208d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // allocate an array to store the results
209d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        byte[] result = new byte[nbChannels * 4/*int to byte*/ * 3/*for mask, azimuth, elevation*/];
210d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
211d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // call into the effect framework
212d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        int status = getParameter(paramsConverter.array(), result);
213d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if (DEBUG) {
214d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            Log.v(TAG, "getAngles(0x" + Integer.toHexString(inputChannelMask) + ", 0x"
215d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    + Integer.toHexString(deviceType) + ") returns " + status);
216d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
217d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
218d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if (status >= 0) {
219d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            if (angles != null) {
220d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                // convert and copy the results
221d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                ByteBuffer resultConverter = ByteBuffer.wrap(result);
222d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                resultConverter.order(ByteOrder.nativeOrder());
223d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                for (int i = 0 ; i < nbChannels ; i++) {
224d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    // write the channel mask
225d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    angles[3 * i] = AudioFormat.convertNativeChannelMaskToOutMask(
226d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                            resultConverter.getInt((i * 4 * 3)));
227d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    // write the azimuth
228d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    angles[3 * i + 1] = resultConverter.getInt(i * 4 * 3 + 4);
229d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    // write the elevation
230d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    angles[3 * i + 2] = resultConverter.getInt(i * 4 * 3 + 8);
231d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    if (DEBUG) {
232d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                        Log.v(TAG, "channel 0x" + Integer.toHexString(angles[3*i]).toUpperCase()
233d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                                + " at az=" + angles[3*i+1] + "deg"
234d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                                + " elev="  + angles[3*i+2] + "deg");
235d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    }
236d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                }
237d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            }
238d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            return true;
239d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        } else if (status == AudioEffect.ERROR_BAD_VALUE) {
240d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            // a BAD_VALUE return from getParameter indicates the configuration is not supported
241d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            // don't throw an exception, just return false
242d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            return false;
243d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        } else {
244d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            // something wrong may have happened
245d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            checkStatus(status);
246d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
247d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // unexpected virtualizer behavior
248d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        Log.e(TAG, "unexpected status code " + status
249d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                + " after getParameter(PARAM_VIRTUAL_SPEAKER_ANGLES)");
250d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        return false;
251d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    }
252d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
253d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
254b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * A virtualization mode indicating virtualization processing is not active.
255b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * See {@link #getVirtualizationMode()} as one of the possible return value.
256b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     */
257b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    public static final int VIRTUALIZATION_MODE_OFF = 0;
258b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi
259b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    /**
260b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * A virtualization mode used to indicate the virtualizer effect must stop forcing the
261b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * processing to a particular mode in {@link #forceVirtualizationMode(int)}.
262b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     */
263b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    public static final int VIRTUALIZATION_MODE_AUTO = 1;
264b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    /**
265b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * A virtualization mode typically used over headphones.
266b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * Binaural virtualization describes an audio processing configuration for virtualization
267b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * where the left and right channels are respectively reaching the left and right ear of the
268b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * user, without also feeding the opposite ear (as is the case when listening over speakers).
269b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * <p>Such a mode is therefore meant to be used when audio is playing over stereo wired
270b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * headphones or headsets, but also stereo headphones through a wireless A2DP Bluetooth link.
271b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * <p>See {@link #canVirtualize(int, int)} to verify this mode is supported by this Virtualizer.
272b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     */
273b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    public final static int VIRTUALIZATION_MODE_BINAURAL = 2;
274b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi
275b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    /**
276b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * A virtualization mode typically used over speakers.
277b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * Transaural virtualization describes an audio processing configuration that differs from
278b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * binaural (as described in {@link #VIRTUALIZATION_MODE_BINAURAL} in that cross-talk is
279b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * present, i.e. audio played from the left channel also reaches the right ear of the user,
280b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * and vice-versa.
281b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * <p>When supported, such a mode is therefore meant to be used when audio is playing over the
282b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * built-in stereo speakers of a device, if they are featured.
283b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * <p>See {@link #canVirtualize(int, int)} to verify this mode is supported by this Virtualizer.
284b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     */
285b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    public final static int VIRTUALIZATION_MODE_TRANSAURAL = 3;
286b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi
287b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    /** @hide */
288b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    @IntDef( {
289b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        VIRTUALIZATION_MODE_BINAURAL,
290b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        VIRTUALIZATION_MODE_TRANSAURAL
291b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    })
292b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    @Retention(RetentionPolicy.SOURCE)
293b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    public @interface VirtualizationMode {}
294b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi
295b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    /** @hide */
296b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    @IntDef( {
297b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        VIRTUALIZATION_MODE_AUTO,
298b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        VIRTUALIZATION_MODE_BINAURAL,
299b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        VIRTUALIZATION_MODE_TRANSAURAL
300b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    })
301b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    @Retention(RetentionPolicy.SOURCE)
302b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    public @interface ForceVirtualizationMode {}
303b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi
304b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    private static int getDeviceForModeQuery(@VirtualizationMode int virtualizationMode)
305b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi            throws IllegalArgumentException {
306b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        switch (virtualizationMode) {
307b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi            case VIRTUALIZATION_MODE_BINAURAL:
308a33be211e768746745a0deeba71f8c6b65e72442Paul McLean                return AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
309b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi            case VIRTUALIZATION_MODE_TRANSAURAL:
310a33be211e768746745a0deeba71f8c6b65e72442Paul McLean                return AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
311b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi            default:
312b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi                throw (new IllegalArgumentException(
313b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi                        "Virtualizer: illegal virtualization mode " + virtualizationMode));
314b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        }
315b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    }
316b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi
317b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    private static int getDeviceForModeForce(@ForceVirtualizationMode int virtualizationMode)
318b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi            throws IllegalArgumentException {
319b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        if (virtualizationMode == VIRTUALIZATION_MODE_AUTO) {
320a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            return AudioDeviceInfo.TYPE_UNKNOWN;
321b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        } else {
322b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi            return getDeviceForModeQuery(virtualizationMode);
323b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        }
324b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    }
325b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi
326b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    private static int deviceToMode(int deviceType) {
327b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        switch (deviceType) {
328a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_WIRED_HEADSET:
329a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
330a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_BLUETOOTH_SCO:
331a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
332b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi                return VIRTUALIZATION_MODE_BINAURAL;
333a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
334a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_LINE_ANALOG:
335a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_LINE_DIGITAL:
336a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
337a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_HDMI:
338a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_HDMI_ARC:
339a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_USB_DEVICE:
340a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_USB_ACCESSORY:
341a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_DOCK:
342a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_FM:
343a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_AUX_LINE:
344b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi                return VIRTUALIZATION_MODE_TRANSAURAL;
345a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            case AudioDeviceInfo.TYPE_UNKNOWN:
346b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi            default:
347b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi                return VIRTUALIZATION_MODE_OFF;
348b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        }
349b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    }
350b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi
351b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    /**
352b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * Checks if the combination of a channel mask and virtualization mode is supported by this
353b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * virtualizer.
354d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Some virtualizer implementations may only support binaural processing (i.e. only support
355b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * headphone output, see {@link #VIRTUALIZATION_MODE_BINAURAL}), some may support transaural
356b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * processing (i.e. for speaker output, see {@link #VIRTUALIZATION_MODE_TRANSAURAL}) for the
357d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * built-in speakers. Use this method to query the virtualizer implementation capabilities.
358d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param inputChannelMask the channel mask of the content to virtualize.
359b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * @param virtualizationMode the mode for which virtualization processing is to be performed,
360b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     *    one of {@link #VIRTUALIZATION_MODE_BINAURAL}, {@link #VIRTUALIZATION_MODE_TRANSAURAL}.
361b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * @return true if the combination of channel mask and virtualization mode is supported, false
362d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    otherwise.
363d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    <br>An indication that a certain channel mask is not supported doesn't necessarily mean
364d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    you cannot play content with that channel mask, it more likely implies the content will
365d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    be downmixed before being virtualized. For instance a virtualizer that only supports a
366d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    mask such as {@link AudioFormat#CHANNEL_OUT_STEREO}
367d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    will still be able to process content with a mask of
368d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    {@link AudioFormat#CHANNEL_OUT_5POINT1}, but will downmix the content to stereo first, and
369d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    then will virtualize, as opposed to virtualizing each channel individually.
370d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalStateException
371d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalArgumentException
372d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws UnsupportedOperationException
373d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
374b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    public boolean canVirtualize(int inputChannelMask, @VirtualizationMode int virtualizationMode)
375d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
376b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        return getAnglesInt(inputChannelMask, getDeviceForModeQuery(virtualizationMode), null);
377d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    }
378d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
379d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
380d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * Queries the virtual speaker angles (azimuth and elevation) for a combination of a channel
381b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * mask and virtualization mode.
382b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * If the virtualization configuration (mask and mode) is supported (see
383d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * {@link #canVirtualize(int, int)}, the array angles will contain upon return the
384d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * definition of each virtual speaker and its azimuth and elevation angles relative to the
385d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * listener.
386d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * <br>Note that in some virtualizer implementations, the angles may be strength-dependent.
387d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param inputChannelMask the channel mask of the content to virtualize.
388b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * @param virtualizationMode the mode for which virtualization processing is to be performed,
389b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     *    one of {@link #VIRTUALIZATION_MODE_BINAURAL}, {@link #VIRTUALIZATION_MODE_TRANSAURAL}.
390d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @param angles a non-null array whose length is 3 times the number of channels in the channel
391d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    mask.
392d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    If the method indicates the configuration is supported, the array will contain upon return
393d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    triplets of values: for each channel <code>i</code> among the channels of the mask:
394d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    <ul>
395d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *      <li>the element at index <code>3*i</code> in the array contains the speaker
396d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *          identification (e.g. {@link AudioFormat#CHANNEL_OUT_FRONT_LEFT}),</li>
397d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *      <li>the element at index <code>3*i+1</code> contains its corresponding azimuth angle
398d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *          expressed in degrees, where 0 is the direction the listener faces, 180 is behind
399d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *          the listener, and -90 is to her/his left,</li>
400d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *      <li>the element at index <code>3*i+2</code> contains its corresponding elevation angle
401d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *          where +90 is directly above the listener, 0 is the horizontal plane, and -90 is
402d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *          directly below the listener.</li>
403b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * @return true if the combination of channel mask and virtualization mode is supported, false
404d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     *    otherwise.
405d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalStateException
406d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalArgumentException
407d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws UnsupportedOperationException
408d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
409b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    public boolean getSpeakerAngles(int inputChannelMask,
410b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi            @VirtualizationMode int virtualizationMode, int[] angles)
411d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
412d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if (angles == null) {
413d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throw (new IllegalArgumentException(
414d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                    "Virtualizer: illegal null channel / angle array"));
415d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
416d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
417b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        return getAnglesInt(inputChannelMask, getDeviceForModeQuery(virtualizationMode), angles);
418d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    }
419d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
420d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
421b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * Forces the virtualizer effect to use the given processing mode.
422d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * The effect must be enabled for the forced mode to be applied.
423b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * @param virtualizationMode one of {@link #VIRTUALIZATION_MODE_BINAURAL},
424b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     *     {@link #VIRTUALIZATION_MODE_TRANSAURAL} to force a particular processing mode, or
425b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     *     {@value #VIRTUALIZATION_MODE_AUTO} to stop forcing a mode.
426b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * @return true if the processing mode is supported, and it is successfully set, or
427b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     *     forcing was successfully disabled, false otherwise.
428d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalStateException
429d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws IllegalArgumentException
430d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     * @throws UnsupportedOperationException
431d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
432b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi    public boolean forceVirtualizationMode(@ForceVirtualizationMode int virtualizationMode)
433d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
434d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // convert Java device type to internal representation
435b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        int deviceType = getDeviceForModeForce(virtualizationMode);
436a33be211e768746745a0deeba71f8c6b65e72442Paul McLean        int internalDevice = AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType);
437d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
438d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        int status = setParameter(PARAM_FORCE_VIRTUALIZATION_MODE, internalDevice);
439d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
440d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if (status >= 0) {
441d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            return true;
442d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        } else if (status == AudioEffect.ERROR_BAD_VALUE) {
443b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi            // a BAD_VALUE return from setParameter indicates the mode can't be forced
444b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi            // don't throw an exception, just return false
445d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            return false;
446d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        } else {
447d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            // something wrong may have happened
448d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            checkStatus(status);
449d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
450d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // unexpected virtualizer behavior
451d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        Log.e(TAG, "unexpected status code " + status
452d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                + " after setParameter(PARAM_FORCE_VIRTUALIZATION_MODE)");
453d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        return false;
454d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    }
455d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
456d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
457b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * Return the virtualization mode being used, if any.
458b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     * @return the virtualization mode being used.
459b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     *     If virtualization is not active, the virtualization mode will be
460b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     *     {@link #VIRTUALIZATION_MODE_OFF}. Otherwise the value will be
461b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     *     {@link #VIRTUALIZATION_MODE_BINAURAL} or {@link #VIRTUALIZATION_MODE_TRANSAURAL}.
462b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     *     Virtualization may not be active either because the effect is not enabled or
463b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     *     because the current output device is not compatible with this virtualization
464b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi     *     implementation.
465a3ee6642d8325f621f267c951c6a524220646610Jean-Michel Trivi     * @throws IllegalStateException
466a3ee6642d8325f621f267c951c6a524220646610Jean-Michel Trivi     * @throws UnsupportedOperationException
467d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi     */
468a3ee6642d8325f621f267c951c6a524220646610Jean-Michel Trivi    public int getVirtualizationMode()
469b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi            throws IllegalStateException, UnsupportedOperationException {
470d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        int[] value = new int[1];
471d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        int status = getParameter(PARAM_VIRTUALIZATION_MODE, value);
472d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        if (status >= 0) {
473a33be211e768746745a0deeba71f8c6b65e72442Paul McLean            return deviceToMode(AudioDeviceInfo.convertInternalDeviceToDeviceType(value[0]));
474d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        } else if (status == AudioEffect.ERROR_BAD_VALUE) {
475b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi            return VIRTUALIZATION_MODE_OFF;
476d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        } else {
477d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            // something wrong may have happened
478d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi            checkStatus(status);
479d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        }
480d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        // unexpected virtualizer behavior
481d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi        Log.e(TAG, "unexpected status code " + status
482d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi                + " after getParameter(PARAM_VIRTUALIZATION_MODE)");
483b3bf7d487afdba540b1ce0f5abc57f060a159d0eJean-Michel Trivi        return VIRTUALIZATION_MODE_OFF;
484d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    }
485d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi
486d2bebb3ab86177c0d27664af86b30b7dce2c9bcbJean-Michel Trivi    /**
487fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * The OnParameterChangeListener interface defines a method called by the Virtualizer when a
488fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * parameter value has changed.
489fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
490fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public interface OnParameterChangeListener  {
491fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        /**
492fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * Method called when a parameter value has changed. The method is called only if the
493fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * parameter was changed by another application having the control of the same
494fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * Virtualizer engine.
495fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * @param effect the Virtualizer on which the interface is registered.
496fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * @param status status of the set parameter operation.
497fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ...
498fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         * @param value the new parameter value.
499fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent         */
500fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        void onParameterChange(Virtualizer effect, int status, int param, short value);
501fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
502fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
503fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
504fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Listener used internally to receive unformatted parameter change events from AudioEffect
505fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * super class.
506fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
507fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
508fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        private BaseParameterListener() {
509fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
510fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        }
511fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
512fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            OnParameterChangeListener l = null;
513fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
514fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            synchronized (mParamListenerLock) {
515fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                if (mParamListener != null) {
516fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                    l = mParamListener;
517fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                }
518fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            }
519fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            if (l != null) {
520fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                int p = -1;
521fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                short v = -1;
522fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
523fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                if (param.length == 4) {
524fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                    p = byteArrayToInt(param, 0);
525fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                }
526fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                if (value.length == 2) {
527fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                    v = byteArrayToShort(value, 0);
528fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                }
529fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                if (p != -1 && v != -1) {
530fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                    l.onParameterChange(Virtualizer.this, status, p, v);
531fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                }
532fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            }
533fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        }
534fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
535fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent
536fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    /**
537fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * Registers an OnParameterChangeListener interface.
538fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     * @param listener OnParameterChangeListener interface registered
539fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent     */
540fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    public void setParameterListener(OnParameterChangeListener listener) {
541fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        synchronized (mParamListenerLock) {
542fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            if (mParamListener == null) {
543fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                mParamListener = listener;
544fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                mBaseParamListener = new BaseParameterListener();
545fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent                super.setParameterListener(mBaseParamListener);
546fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent            }
547fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent        }
548fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent    }
549ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
550ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    /**
551ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * The Settings class regroups all virtualizer parameters. It is used in
552ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * conjuntion with getProperties() and setProperties() methods to backup and restore
553ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * all parameters in a single call.
554ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     */
555ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    public static class Settings {
556ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        public short strength;
557ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
558ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        public Settings() {
559ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        }
560ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
561ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        /**
562ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent         * Settings class constructor from a key=value; pairs formatted string. The string is
563ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent         * typically returned by Settings.toString() method.
564ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent         * @throws IllegalArgumentException if the string is not correctly formatted.
565ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent         */
566ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        public Settings(String settings) {
567ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            StringTokenizer st = new StringTokenizer(settings, "=;");
568ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            int tokens = st.countTokens();
569ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            if (st.countTokens() != 3) {
570ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                throw new IllegalArgumentException("settings: " + settings);
571ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            }
572ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            String key = st.nextToken();
573ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            if (!key.equals("Virtualizer")) {
574ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                throw new IllegalArgumentException(
575ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                        "invalid settings for Virtualizer: " + key);
576ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            }
577ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            try {
578ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                key = st.nextToken();
579ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                if (!key.equals("strength")) {
580ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                    throw new IllegalArgumentException("invalid key name: " + key);
581ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                }
582ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                strength = Short.parseShort(st.nextToken());
583ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent             } catch (NumberFormatException nfe) {
584ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                throw new IllegalArgumentException("invalid value for key: " + key);
585ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            }
586ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        }
587ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
588ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        @Override
589ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        public String toString() {
590ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            String str = new String (
591ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                    "Virtualizer"+
592ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                    ";strength="+Short.toString(strength)
593ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent                    );
594ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent            return str;
595ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        }
596ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    };
597ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
598ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
599ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    /**
600ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * Gets the virtualizer properties. This method is useful when a snapshot of current
601ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * virtualizer settings must be saved by the application.
602ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @return a Virtualizer.Settings object containing all current parameters values
603ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @throws IllegalStateException
604ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @throws IllegalArgumentException
605ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @throws UnsupportedOperationException
606ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     */
607ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    public Virtualizer.Settings getProperties()
608ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
609ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        Settings settings = new Settings();
610ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        short[] value = new short[1];
611ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        checkStatus(getParameter(PARAM_STRENGTH, value));
612ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        settings.strength = value[0];
613ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        return settings;
614ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    }
615ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent
616ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    /**
617ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * Sets the virtualizer properties. This method is useful when virtualizer settings have to
618ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * be applied from a previous backup.
61917cb280e7f1ac3556eac90ab08263712e0348cb9Eric Laurent     * @param settings a Virtualizer.Settings object containing the properties to apply
620ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @throws IllegalStateException
621ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @throws IllegalArgumentException
622ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     * @throws UnsupportedOperationException
623ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent     */
624ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    public void setProperties(Virtualizer.Settings settings)
625ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
626ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent        checkStatus(setParameter(PARAM_STRENGTH, settings.strength));
627ca57d1cc89d65dfbd59c749c5736574cd08c7bd3Eric Laurent    }
628fd84f97af4b44d54bba53bb85b31a6dbce07f6e2Eric Laurent}
629