Home | History | Annotate | Download | only in audiofx
      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      */
    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