Equalizer.java revision 62f3617f2f4016ad2f59635d5156d64872989880
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 Equalizer is used to alter the frequency response of a particular music source or of the main
34 * output mix.
35 * <p>An application creates an Equalizer object to instantiate and control an Equalizer engine
36 * in the audio framework. The application can either simply use predefined presets or have a more
37 * precise control of the gain in each frequency band controlled by the equalizer.
38 * <p>The methods, parameter types and units exposed by the Equalizer implementation are directly
39 * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/)
40 * for the SLEqualizerItf interface. Please refer to this specification for more details.
41 * <p>To attach the Equalizer to a particular AudioTrack or MediaPlayer, specify the audio session
42 * ID of this AudioTrack or MediaPlayer when constructing the Equalizer.
43 * <p>NOTE: attaching an Equalizer to the global audio output mix by use of session 0 is deprecated.
44 * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
45 * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling audio
46 * effects.
47 */
48
49public class Equalizer extends AudioEffect {
50
51    private final static String TAG = "Equalizer";
52
53    // These constants must be synchronized with those in
54    // frameworks/base/include/media/EffectEqualizerApi.h
55    /**
56     * Number of bands. Parameter ID for OnParameterChangeListener
57     */
58    public static final int PARAM_NUM_BANDS = 0;
59    /**
60     * Band level range. Parameter ID for OnParameterChangeListener
61     */
62    public static final int PARAM_LEVEL_RANGE = 1;
63    /**
64     * Band level. Parameter ID for OnParameterChangeListener
65     */
66    public static final int PARAM_BAND_LEVEL = 2;
67    /**
68     * Band center frequency. Parameter ID for OnParameterChangeListener
69     */
70    public static final int PARAM_CENTER_FREQ = 3;
71    /**
72     * Band frequency range. Parameter ID for
73     * {@link android.media.audiofx.Equalizer.OnParameterChangeListener}
74     */
75    public static final int PARAM_BAND_FREQ_RANGE = 4;
76    /**
77     * Band for a given frequency. Parameter ID for OnParameterChangeListener
78     *
79     */
80    public static final int PARAM_GET_BAND = 5;
81    /**
82     * Current preset. Parameter ID for OnParameterChangeListener
83     */
84    public static final int PARAM_CURRENT_PRESET = 6;
85    /**
86     * Request number of presets. Parameter ID for OnParameterChangeListener
87     */
88    public static final int PARAM_GET_NUM_OF_PRESETS = 7;
89    /**
90     * Request preset name. Parameter ID for OnParameterChangeListener
91     */
92    public static final int PARAM_GET_PRESET_NAME = 8;
93    // used by setProperties()/getProperties
94    private static final int PARAM_PROPERTIES = 9;
95    /**
96     * Maximum size for preset name
97     */
98    public static final int PARAM_STRING_SIZE_MAX = 32;
99
100    /**
101     * Number of bands implemented by Equalizer engine
102     */
103    private short mNumBands = 0;
104
105    /**
106     * Number of presets implemented by Equalizer engine
107     */
108    private int mNumPresets;
109    /**
110     * Names of presets implemented by Equalizer engine
111     */
112    private String[] mPresetNames;
113
114    /**
115     * Registered listener for parameter changes.
116     */
117    private OnParameterChangeListener mParamListener = null;
118
119    /**
120     * Listener used internally to to receive raw parameter change event from AudioEffect super class
121     */
122    private BaseParameterListener mBaseParamListener = null;
123
124    /**
125     * Lock for access to mParamListener
126     */
127    private final Object mParamListenerLock = new Object();
128
129    /**
130     * Class constructor.
131     * @param priority the priority level requested by the application for controlling the Equalizer
132     * engine. As the same engine can be shared by several applications, this parameter indicates
133     * how much the requesting application needs control of effect parameters. The normal priority
134     * is 0, above normal is a positive number, below normal a negative number.
135     * @param audioSession  system wide unique audio session identifier. The Equalizer will be
136     * attached to the MediaPlayer or AudioTrack in the same audio session.
137     *
138     * @throws java.lang.IllegalStateException
139     * @throws java.lang.IllegalArgumentException
140     * @throws java.lang.UnsupportedOperationException
141     * @throws java.lang.RuntimeException
142     */
143    public Equalizer(int priority, int audioSession)
144    throws IllegalStateException, IllegalArgumentException,
145           UnsupportedOperationException, RuntimeException {
146        super(EFFECT_TYPE_EQUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
147
148        if (audioSession == 0) {
149            Log.w(TAG, "WARNING: attaching an Equalizer to global output mix is deprecated!");
150        }
151
152        getNumberOfBands();
153
154        mNumPresets = (int)getNumberOfPresets();
155
156        if (mNumPresets != 0) {
157            mPresetNames = new String[mNumPresets];
158            byte[] value = new byte[PARAM_STRING_SIZE_MAX];
159            int[] param = new int[2];
160            param[0] = PARAM_GET_PRESET_NAME;
161            for (int i = 0; i < mNumPresets; i++) {
162                param[1] = i;
163                checkStatus(getParameter(param, value));
164                int length = 0;
165                while (value[length] != 0) length++;
166                try {
167                    mPresetNames[i] = new String(value, 0, length, "ISO-8859-1");
168                } catch (java.io.UnsupportedEncodingException e) {
169                    Log.e(TAG, "preset name decode error");
170                }
171            }
172        }
173    }
174
175    /**
176     * Gets the number of frequency bands supported by the Equalizer engine.
177     * @return the number of bands
178     * @throws IllegalStateException
179     * @throws IllegalArgumentException
180     * @throws UnsupportedOperationException
181     */
182    public short getNumberOfBands()
183    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
184        if (mNumBands != 0) {
185            return mNumBands;
186        }
187        int[] param = new int[1];
188        param[0] = PARAM_NUM_BANDS;
189        short[] result = new short[1];
190        checkStatus(getParameter(param, result));
191        mNumBands = result[0];
192        return mNumBands;
193    }
194
195    /**
196     * Gets the level range for use by {@link #setBandLevel(short,short)}. The level is expressed in
197     * milliBel.
198     * @return the band level range in an array of short integers. The first element is the lower
199     * limit of the range, the second element the upper limit.
200     * @throws IllegalStateException
201     * @throws IllegalArgumentException
202     * @throws UnsupportedOperationException
203     */
204    public short[] getBandLevelRange()
205    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
206        short[] result = new short[2];
207        checkStatus(getParameter(PARAM_LEVEL_RANGE, result));
208        return result;
209    }
210
211    /**
212     * Sets the given equalizer band to the given gain value.
213     * @param band frequency band that will have the new gain. The numbering of the bands starts
214     * from 0 and ends at (number of bands - 1).
215     * @param level new gain in millibels that will be set to the given band. getBandLevelRange()
216     * will define the maximum and minimum values.
217     * @throws IllegalStateException
218     * @throws IllegalArgumentException
219     * @throws UnsupportedOperationException
220     * @see #getNumberOfBands()
221     */
222    public void setBandLevel(short band, short level)
223    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
224        int[] param = new int[2];
225        short[] value = new short[1];
226
227        param[0] = PARAM_BAND_LEVEL;
228        param[1] = (int)band;
229        value[0] = level;
230        checkStatus(setParameter(param, value));
231    }
232
233    /**
234     * Gets the gain set for the given equalizer band.
235     * @param band frequency band whose gain is requested. The numbering of the bands starts
236     * from 0 and ends at (number of bands - 1).
237     * @return the gain in millibels of the given band.
238     * @throws IllegalStateException
239     * @throws IllegalArgumentException
240     * @throws UnsupportedOperationException
241     */
242    public short getBandLevel(short band)
243    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
244        int[] param = new int[2];
245        short[] result = new short[1];
246
247        param[0] = PARAM_BAND_LEVEL;
248        param[1] = (int)band;
249        checkStatus(getParameter(param, result));
250
251        return result[0];
252    }
253
254
255    /**
256     * Gets the center frequency of the given band.
257     * @param band frequency band whose center frequency is requested. The numbering of the bands
258     * starts from 0 and ends at (number of bands - 1).
259     * @return the center frequency in milliHertz
260     * @throws IllegalStateException
261     * @throws IllegalArgumentException
262     * @throws UnsupportedOperationException
263     */
264    public int getCenterFreq(short band)
265    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
266        int[] param = new int[2];
267        int[] result = new int[1];
268
269        param[0] = PARAM_CENTER_FREQ;
270        param[1] = (int)band;
271        checkStatus(getParameter(param, result));
272
273        return result[0];
274    }
275
276    /**
277     * Gets the frequency range of the given frequency band.
278     * @param band frequency band whose frequency range is requested. The numbering of the bands
279     * starts from 0 and ends at (number of bands - 1).
280     * @return the frequency range in millHertz in an array of integers. The first element is the
281     * lower limit of the range, the second element the upper limit.
282     * @throws IllegalStateException
283     * @throws IllegalArgumentException
284     * @throws UnsupportedOperationException
285     */
286    public int[] getBandFreqRange(short band)
287    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
288        int[] param = new int[2];
289        int[] result = new int[2];
290        param[0] = PARAM_BAND_FREQ_RANGE;
291        param[1] = (int)band;
292        checkStatus(getParameter(param, result));
293
294        return result;
295    }
296
297    /**
298     * Gets the band that has the most effect on the given frequency.
299     * @param frequency frequency in milliHertz which is to be equalized via the returned band.
300     * @return the frequency band that has most effect on the given frequency.
301     * @throws IllegalStateException
302     * @throws IllegalArgumentException
303     * @throws UnsupportedOperationException
304     */
305    public short getBand(int frequency)
306    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
307        int[] param = new int[2];
308        short[] result = new short[1];
309
310        param[0] = PARAM_GET_BAND;
311        param[1] = frequency;
312        checkStatus(getParameter(param, result));
313
314        return result[0];
315    }
316
317    /**
318     * Gets current preset.
319     * @return the preset that is set at the moment.
320     * @throws IllegalStateException
321     * @throws IllegalArgumentException
322     * @throws UnsupportedOperationException
323     */
324    public short getCurrentPreset()
325    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
326        short[] result = new short[1];
327        checkStatus(getParameter(PARAM_CURRENT_PRESET, result));
328        return result[0];
329    }
330
331    /**
332     * Sets the equalizer according to the given preset.
333     * @param preset new preset that will be taken into use. The valid range is [0,
334     * number of presets-1].
335     * @throws IllegalStateException
336     * @throws IllegalArgumentException
337     * @throws UnsupportedOperationException
338     * @see #getNumberOfPresets()
339     */
340    public void usePreset(short preset)
341    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
342        checkStatus(setParameter(PARAM_CURRENT_PRESET, preset));
343    }
344
345    /**
346     * Gets the total number of presets the equalizer supports. The presets will have indices
347     * [0, number of presets-1].
348     * @return the number of presets the equalizer supports.
349     * @throws IllegalStateException
350     * @throws IllegalArgumentException
351     * @throws UnsupportedOperationException
352     */
353    public short getNumberOfPresets()
354    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
355        short[] result = new short[1];
356        checkStatus(getParameter(PARAM_GET_NUM_OF_PRESETS, result));
357        return result[0];
358    }
359
360    /**
361     * Gets the preset name based on the index.
362     * @param preset index of the preset. The valid range is [0, number of presets-1].
363     * @return a string containing the name of the given preset.
364     * @throws IllegalStateException
365     * @throws IllegalArgumentException
366     * @throws UnsupportedOperationException
367     */
368    public String getPresetName(short preset)
369    {
370        if (preset >= 0 && preset < mNumPresets) {
371            return mPresetNames[preset];
372        } else {
373            return "";
374        }
375    }
376
377    /**
378     * The OnParameterChangeListener interface defines a method called by the Equalizer when a
379     * parameter value has changed.
380     */
381    public interface OnParameterChangeListener  {
382        /**
383         * Method called when a parameter value has changed. The method is called only if the
384         * parameter was changed by another application having the control of the same
385         * Equalizer engine.
386         * @param effect the Equalizer on which the interface is registered.
387         * @param status status of the set parameter operation.
388         * @param param1 ID of the modified parameter. See {@link #PARAM_BAND_LEVEL} ...
389         * @param param2 additional parameter qualifier (e.g the band for band level parameter).
390         * @param value the new parameter value.
391         */
392        void onParameterChange(Equalizer effect, int status, int param1, int param2, int value);
393    }
394
395    /**
396     * Listener used internally to receive unformatted parameter change events from AudioEffect
397     * super class.
398     */
399    private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
400        private BaseParameterListener() {
401
402        }
403        public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
404            OnParameterChangeListener l = null;
405
406            synchronized (mParamListenerLock) {
407                if (mParamListener != null) {
408                    l = mParamListener;
409                }
410            }
411            if (l != null) {
412                int p1 = -1;
413                int p2 = -1;
414                int v = -1;
415
416                if (param.length >= 4) {
417                    p1 = byteArrayToInt(param, 0);
418                    if (param.length >= 8) {
419                        p2 = byteArrayToInt(param, 4);
420                    }
421                }
422                if (value.length == 2) {
423                    v = (int)byteArrayToShort(value, 0);;
424                } else if (value.length == 4) {
425                    v = byteArrayToInt(value, 0);
426                }
427
428                if (p1 != -1 && v != -1) {
429                    l.onParameterChange(Equalizer.this, status, p1, p2, v);
430                }
431            }
432        }
433    }
434
435    /**
436     * Registers an OnParameterChangeListener interface.
437     * @param listener OnParameterChangeListener interface registered
438     */
439    public void setParameterListener(OnParameterChangeListener listener) {
440        synchronized (mParamListenerLock) {
441            if (mParamListener == null) {
442                mParamListener = listener;
443                mBaseParamListener = new BaseParameterListener();
444                super.setParameterListener(mBaseParamListener);
445            }
446        }
447    }
448
449    /**
450     * The Settings class regroups all equalizer parameters. It is used in
451     * conjuntion with getProperties() and setProperties() methods to backup and restore
452     * all parameters in a single call.
453     */
454    public static class Settings {
455        public short curPreset;
456        public short numBands = 0;
457        public short[] bandLevels = null;
458
459        public Settings() {
460        }
461
462        /**
463         * Settings class constructor from a key=value; pairs formatted string. The string is
464         * typically returned by Settings.toString() method.
465         * @throws IllegalArgumentException if the string is not correctly formatted.
466         */
467        public Settings(String settings) {
468            StringTokenizer st = new StringTokenizer(settings, "=;");
469            int tokens = st.countTokens();
470            if (st.countTokens() < 5) {
471                throw new IllegalArgumentException("settings: " + settings);
472            }
473            String key = st.nextToken();
474            if (!key.equals("Equalizer")) {
475                throw new IllegalArgumentException(
476                        "invalid settings for Equalizer: " + key);
477            }
478            try {
479                key = st.nextToken();
480                if (!key.equals("curPreset")) {
481                    throw new IllegalArgumentException("invalid key name: " + key);
482                }
483                curPreset = Short.parseShort(st.nextToken());
484                key = st.nextToken();
485                if (!key.equals("numBands")) {
486                    throw new IllegalArgumentException("invalid key name: " + key);
487                }
488                numBands = Short.parseShort(st.nextToken());
489                if (st.countTokens() != numBands*2) {
490                    throw new IllegalArgumentException("settings: " + settings);
491                }
492                bandLevels = new short[numBands];
493                for (int i = 0; i < numBands; i++) {
494                    key = st.nextToken();
495                    if (!key.equals("band"+(i+1)+"Level")) {
496                        throw new IllegalArgumentException("invalid key name: " + key);
497                    }
498                    bandLevels[i] = Short.parseShort(st.nextToken());
499                }
500             } catch (NumberFormatException nfe) {
501                throw new IllegalArgumentException("invalid value for key: " + key);
502            }
503        }
504
505        @Override
506        public String toString() {
507
508            String str = new String (
509                    "Equalizer"+
510                    ";curPreset="+Short.toString(curPreset)+
511                    ";numBands="+Short.toString(numBands)
512                    );
513            for (int i = 0; i < numBands; i++) {
514                str = str.concat(";band"+(i+1)+"Level="+Short.toString(bandLevels[i]));
515            }
516            return str;
517        }
518    };
519
520
521    /**
522     * Gets the equalizer properties. This method is useful when a snapshot of current
523     * equalizer settings must be saved by the application.
524     * @return an Equalizer.Settings object containing all current parameters values
525     * @throws IllegalStateException
526     * @throws IllegalArgumentException
527     * @throws UnsupportedOperationException
528     */
529    public Equalizer.Settings getProperties()
530    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
531        byte[] param = new byte[4 + mNumBands * 2];
532        checkStatus(getParameter(PARAM_PROPERTIES, param));
533        Settings settings = new Settings();
534        settings.curPreset = byteArrayToShort(param, 0);
535        settings.numBands = byteArrayToShort(param, 2);
536        settings.bandLevels = new short[mNumBands];
537        for (int i = 0; i < mNumBands; i++) {
538            settings.bandLevels[i] = byteArrayToShort(param, 4 + 2*i);
539        }
540        return settings;
541    }
542
543    /**
544     * Sets the equalizer properties. This method is useful when equalizer settings have to
545     * be applied from a previous backup.
546     * @param settings an Equalizer.Settings object containing the properties to apply
547     * @throws IllegalStateException
548     * @throws IllegalArgumentException
549     * @throws UnsupportedOperationException
550     */
551    public void setProperties(Equalizer.Settings settings)
552    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
553        if (settings.numBands != settings.bandLevels.length ||
554            settings.numBands != mNumBands) {
555            throw new IllegalArgumentException("settings invalid band count: " +settings.numBands);
556        }
557
558        byte[] param = concatArrays(shortToByteArray(settings.curPreset),
559                                    shortToByteArray(mNumBands));
560        for (int i = 0; i < mNumBands; i++) {
561            param = concatArrays(param,
562                                 shortToByteArray(settings.bandLevels[i]));
563        }
564        checkStatus(setParameter(PARAM_PROPERTIES, param));
565    }
566}
567