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