1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.media.audiofx;
18
19import android.app.Activity;
20import android.content.Context;
21import android.content.Intent;
22import android.media.audiofx.AudioEffect;
23import android.os.Bundle;
24import android.util.Log;
25
26import java.nio.ByteOrder;
27import java.nio.ByteBuffer;
28import java.nio.CharBuffer;
29import java.util.StringTokenizer;
30
31
32/**
33 * An audio virtualizer is a general name for an effect to spatialize audio channels. The exact
34 * behavior of this effect is dependent on the number of audio input channels and the types and
35 * number of audio output channels of the device. For example, in the case of a stereo input and
36 * stereo headphone output, a stereo widening effect is used when this effect is turned on.
37 * <p>An application creates a Virtualizer object to instantiate and control a virtualizer engine
38 * in the audio framework.
39 * <p>The methods, parameter types and units exposed by the Virtualizer implementation are directly
40 * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/)
41 * for the SLVirtualizerItf interface. Please refer to this specification for more details.
42 * <p>To attach the Virtualizer to a particular AudioTrack or MediaPlayer, specify the audio session
43 * ID of this AudioTrack or MediaPlayer when constructing the Virtualizer.
44 * <p>NOTE: attaching a Virtualizer to the global audio output mix by use of session 0 is
45 * deprecated.
46 * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
47 * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
48 * audio effects.
49 */
50
51public class Virtualizer extends AudioEffect {
52
53    private final static String TAG = "Virtualizer";
54
55    // These constants must be synchronized with those in frameworks/base/include/media/EffectVirtualizerApi.h
56    /**
57     * Is strength parameter supported by virtualizer engine. Parameter ID for getParameter().
58     */
59    public static final int PARAM_STRENGTH_SUPPORTED = 0;
60    /**
61     * Virtualizer effect strength. Parameter ID for
62     * {@link android.media.audiofx.Virtualizer.OnParameterChangeListener}
63     */
64    public static final int PARAM_STRENGTH = 1;
65
66    /**
67     * Indicates if strength parameter is supported by the virtualizer engine
68     */
69    private boolean mStrengthSupported = false;
70
71    /**
72     * Registered listener for parameter changes.
73     */
74    private OnParameterChangeListener mParamListener = null;
75
76    /**
77     * Listener used internally to to receive raw parameter change event from AudioEffect super class
78     */
79    private BaseParameterListener mBaseParamListener = null;
80
81    /**
82     * Lock for access to mParamListener
83     */
84    private final Object mParamListenerLock = new Object();
85
86    /**
87     * Class constructor.
88     * @param priority the priority level requested by the application for controlling the Virtualizer
89     * engine. As the same engine can be shared by several applications, this parameter indicates
90     * how much the requesting application needs control of effect parameters. The normal priority
91     * is 0, above normal is a positive number, below normal a negative number.
92     * @param audioSession  system wide unique audio session identifier. The Virtualizer will
93     * be attached to the MediaPlayer or AudioTrack in the same audio session.
94     *
95     * @throws java.lang.IllegalStateException
96     * @throws java.lang.IllegalArgumentException
97     * @throws java.lang.UnsupportedOperationException
98     * @throws java.lang.RuntimeException
99     */
100    public Virtualizer(int priority, int audioSession)
101    throws IllegalStateException, IllegalArgumentException,
102           UnsupportedOperationException, RuntimeException {
103        super(EFFECT_TYPE_VIRTUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
104
105        if (audioSession == 0) {
106            Log.w(TAG, "WARNING: attaching a Virtualizer to global output mix is deprecated!");
107        }
108
109        int[] value = new int[1];
110        checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
111        mStrengthSupported = (value[0] != 0);
112    }
113
114    /**
115     * Indicates whether setting strength is supported. If this method returns false, only one
116     * strength is supported and the setStrength() method always rounds to that value.
117     * @return true is strength parameter is supported, false otherwise
118     */
119    public boolean getStrengthSupported() {
120       return mStrengthSupported;
121    }
122
123    /**
124     * Sets the strength of the virtualizer effect. If the implementation does not support per mille
125     * accuracy for setting the strength, it is allowed to round the given strength to the nearest
126     * supported value. You can use the {@link #getRoundedStrength()} method to query the
127     * (possibly rounded) value that was actually set.
128     * @param strength strength of the effect. The valid range for strength strength is [0, 1000],
129     * where 0 per mille designates the mildest effect and 1000 per mille designates the strongest.
130     * @throws IllegalStateException
131     * @throws IllegalArgumentException
132     * @throws UnsupportedOperationException
133     */
134    public void setStrength(short strength)
135    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
136        checkStatus(setParameter(PARAM_STRENGTH, strength));
137    }
138
139    /**
140     * Gets the current strength of the effect.
141     * @return the strength of the effect. The valid range for strength is [0, 1000], where 0 per
142     * mille designates the mildest effect and 1000 per mille the strongest
143     * @throws IllegalStateException
144     * @throws IllegalArgumentException
145     * @throws UnsupportedOperationException
146     */
147    public short getRoundedStrength()
148    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
149        short[] value = new short[1];
150        checkStatus(getParameter(PARAM_STRENGTH, value));
151        return value[0];
152    }
153
154    /**
155     * The OnParameterChangeListener interface defines a method called by the Virtualizer when a
156     * parameter value has changed.
157     */
158    public interface OnParameterChangeListener  {
159        /**
160         * Method called when a parameter value has changed. The method is called only if the
161         * parameter was changed by another application having the control of the same
162         * Virtualizer engine.
163         * @param effect the Virtualizer on which the interface is registered.
164         * @param status status of the set parameter operation.
165         * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ...
166         * @param value the new parameter value.
167         */
168        void onParameterChange(Virtualizer effect, int status, int param, short value);
169    }
170
171    /**
172     * Listener used internally to receive unformatted parameter change events from AudioEffect
173     * super class.
174     */
175    private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
176        private BaseParameterListener() {
177
178        }
179        public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
180            OnParameterChangeListener l = null;
181
182            synchronized (mParamListenerLock) {
183                if (mParamListener != null) {
184                    l = mParamListener;
185                }
186            }
187            if (l != null) {
188                int p = -1;
189                short v = -1;
190
191                if (param.length == 4) {
192                    p = byteArrayToInt(param, 0);
193                }
194                if (value.length == 2) {
195                    v = byteArrayToShort(value, 0);
196                }
197                if (p != -1 && v != -1) {
198                    l.onParameterChange(Virtualizer.this, status, p, v);
199                }
200            }
201        }
202    }
203
204    /**
205     * Registers an OnParameterChangeListener interface.
206     * @param listener OnParameterChangeListener interface registered
207     */
208    public void setParameterListener(OnParameterChangeListener listener) {
209        synchronized (mParamListenerLock) {
210            if (mParamListener == null) {
211                mParamListener = listener;
212                mBaseParamListener = new BaseParameterListener();
213                super.setParameterListener(mBaseParamListener);
214            }
215        }
216    }
217
218    /**
219     * The Settings class regroups all virtualizer parameters. It is used in
220     * conjuntion with getProperties() and setProperties() methods to backup and restore
221     * all parameters in a single call.
222     */
223    public static class Settings {
224        public short strength;
225
226        public Settings() {
227        }
228
229        /**
230         * Settings class constructor from a key=value; pairs formatted string. The string is
231         * typically returned by Settings.toString() method.
232         * @throws IllegalArgumentException if the string is not correctly formatted.
233         */
234        public Settings(String settings) {
235            StringTokenizer st = new StringTokenizer(settings, "=;");
236            int tokens = st.countTokens();
237            if (st.countTokens() != 3) {
238                throw new IllegalArgumentException("settings: " + settings);
239            }
240            String key = st.nextToken();
241            if (!key.equals("Virtualizer")) {
242                throw new IllegalArgumentException(
243                        "invalid settings for Virtualizer: " + key);
244            }
245            try {
246                key = st.nextToken();
247                if (!key.equals("strength")) {
248                    throw new IllegalArgumentException("invalid key name: " + key);
249                }
250                strength = Short.parseShort(st.nextToken());
251             } catch (NumberFormatException nfe) {
252                throw new IllegalArgumentException("invalid value for key: " + key);
253            }
254        }
255
256        @Override
257        public String toString() {
258            String str = new String (
259                    "Virtualizer"+
260                    ";strength="+Short.toString(strength)
261                    );
262            return str;
263        }
264    };
265
266
267    /**
268     * Gets the virtualizer properties. This method is useful when a snapshot of current
269     * virtualizer settings must be saved by the application.
270     * @return a Virtualizer.Settings object containing all current parameters values
271     * @throws IllegalStateException
272     * @throws IllegalArgumentException
273     * @throws UnsupportedOperationException
274     */
275    public Virtualizer.Settings getProperties()
276    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
277        Settings settings = new Settings();
278        short[] value = new short[1];
279        checkStatus(getParameter(PARAM_STRENGTH, value));
280        settings.strength = value[0];
281        return settings;
282    }
283
284    /**
285     * Sets the virtualizer properties. This method is useful when virtualizer settings have to
286     * be applied from a previous backup.
287     * @param settings a Virtualizer.Settings object containing the properties to apply
288     * @throws IllegalStateException
289     * @throws IllegalArgumentException
290     * @throws UnsupportedOperationException
291     */
292    public void setProperties(Virtualizer.Settings settings)
293    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
294        checkStatus(setParameter(PARAM_STRENGTH, settings.strength));
295    }
296}
297