• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.media.audiofx;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.media.audiofx.AudioEffect;
23 import android.os.Bundle;
24 import android.util.Log;
25 
26 import java.nio.ByteOrder;
27 import java.nio.ByteBuffer;
28 import java.nio.CharBuffer;
29 import 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 
49 public 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      */
Equalizer(int priority, int audioSession)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      */
getNumberOfBands()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      */
getBandLevelRange()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      */
setBandLevel(short band, short level)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      */
getBandLevel(short band)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      */
getCenterFreq(short band)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      */
getBandFreqRange(short band)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      */
getBand(int frequency)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      */
getCurrentPreset()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      */
usePreset(short preset)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      */
getNumberOfPresets()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      */
getPresetName(short preset)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          */
onParameterChange(Equalizer effect, int status, int param1, int param2, int value)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 {
BaseParameterListener()400         private BaseParameterListener() {
401 
402         }
onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value)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      */
setParameterListener(OnParameterChangeListener listener)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 
Settings()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          */
Settings(String settings)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
toString()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      */
getProperties()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      */
setProperties(Equalizer.Settings settings)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