/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media.audiofx; import android.media.audiofx.AudioEffect; import android.util.Log; import java.util.StringTokenizer; /** * An Equalizer is used to alter the frequency response of a particular music source or of the main * output mix. *

An application creates an Equalizer object to instantiate and control an Equalizer engine * in the audio framework. The application can either simply use predefined presets or have a more * precise control of the gain in each frequency band controlled by the equalizer. *

The methods, parameter types and units exposed by the Equalizer implementation are directly * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/) * for the SLEqualizerItf interface. Please refer to this specification for more details. *

To attach the Equalizer to a particular AudioTrack or MediaPlayer, specify the audio session * ID of this AudioTrack or MediaPlayer when constructing the Equalizer. *

NOTE: attaching an Equalizer to the global audio output mix by use of session 0 is deprecated. *

See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions. *

See {@link android.media.audiofx.AudioEffect} class for more details on controlling audio * effects. */ public class Equalizer extends AudioEffect { private final static String TAG = "Equalizer"; // These constants must be synchronized with those in // frameworks/base/include/media/EffectEqualizerApi.h /** * Number of bands. Parameter ID for OnParameterChangeListener */ public static final int PARAM_NUM_BANDS = 0; /** * Band level range. Parameter ID for OnParameterChangeListener */ public static final int PARAM_LEVEL_RANGE = 1; /** * Band level. Parameter ID for OnParameterChangeListener */ public static final int PARAM_BAND_LEVEL = 2; /** * Band center frequency. Parameter ID for OnParameterChangeListener */ public static final int PARAM_CENTER_FREQ = 3; /** * Band frequency range. Parameter ID for * {@link android.media.audiofx.Equalizer.OnParameterChangeListener} */ public static final int PARAM_BAND_FREQ_RANGE = 4; /** * Band for a given frequency. Parameter ID for OnParameterChangeListener * */ public static final int PARAM_GET_BAND = 5; /** * Current preset. Parameter ID for OnParameterChangeListener */ public static final int PARAM_CURRENT_PRESET = 6; /** * Request number of presets. Parameter ID for OnParameterChangeListener */ public static final int PARAM_GET_NUM_OF_PRESETS = 7; /** * Request preset name. Parameter ID for OnParameterChangeListener */ public static final int PARAM_GET_PRESET_NAME = 8; // used by setProperties()/getProperties private static final int PARAM_PROPERTIES = 9; /** * Maximum size for preset name */ public static final int PARAM_STRING_SIZE_MAX = 32; /** * Number of bands implemented by Equalizer engine */ private short mNumBands = 0; /** * Number of presets implemented by Equalizer engine */ private int mNumPresets; /** * Names of presets implemented by Equalizer engine */ private String[] mPresetNames; /** * Registered listener for parameter changes. */ private OnParameterChangeListener mParamListener = null; /** * Listener used internally to to receive raw parameter change event from AudioEffect super class */ private BaseParameterListener mBaseParamListener = null; /** * Lock for access to mParamListener */ private final Object mParamListenerLock = new Object(); /** * Class constructor. * @param priority the priority level requested by the application for controlling the Equalizer * engine. As the same engine can be shared by several applications, this parameter indicates * how much the requesting application needs control of effect parameters. The normal priority * is 0, above normal is a positive number, below normal a negative number. * @param audioSession system wide unique audio session identifier. The Equalizer will be * attached to the MediaPlayer or AudioTrack in the same audio session. * * @throws java.lang.IllegalStateException * @throws java.lang.IllegalArgumentException * @throws java.lang.UnsupportedOperationException * @throws java.lang.RuntimeException */ public Equalizer(int priority, int audioSession) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException, RuntimeException { super(EFFECT_TYPE_EQUALIZER, EFFECT_TYPE_NULL, priority, audioSession); if (audioSession == 0) { Log.w(TAG, "WARNING: attaching an Equalizer to global output mix is deprecated!"); } getNumberOfBands(); mNumPresets = (int)getNumberOfPresets(); if (mNumPresets != 0) { mPresetNames = new String[mNumPresets]; byte[] value = new byte[PARAM_STRING_SIZE_MAX]; int[] param = new int[2]; param[0] = PARAM_GET_PRESET_NAME; for (int i = 0; i < mNumPresets; i++) { param[1] = i; checkStatus(getParameter(param, value)); int length = 0; while (value[length] != 0) length++; try { mPresetNames[i] = new String(value, 0, length, "ISO-8859-1"); } catch (java.io.UnsupportedEncodingException e) { Log.e(TAG, "preset name decode error"); } } } } /** * Gets the number of frequency bands supported by the Equalizer engine. * @return the number of bands * @throws IllegalStateException * @throws IllegalArgumentException * @throws UnsupportedOperationException */ public short getNumberOfBands() throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { if (mNumBands != 0) { return mNumBands; } int[] param = new int[1]; param[0] = PARAM_NUM_BANDS; short[] result = new short[1]; checkStatus(getParameter(param, result)); mNumBands = result[0]; return mNumBands; } /** * Gets the level range for use by {@link #setBandLevel(short,short)}. The level is expressed in * milliBel. * @return the band level range in an array of short integers. The first element is the lower * limit of the range, the second element the upper limit. * @throws IllegalStateException * @throws IllegalArgumentException * @throws UnsupportedOperationException */ public short[] getBandLevelRange() throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { short[] result = new short[2]; checkStatus(getParameter(PARAM_LEVEL_RANGE, result)); return result; } /** * Sets the given equalizer band to the given gain value. * @param band frequency band that will have the new gain. The numbering of the bands starts * from 0 and ends at (number of bands - 1). * @param level new gain in millibels that will be set to the given band. getBandLevelRange() * will define the maximum and minimum values. * @throws IllegalStateException * @throws IllegalArgumentException * @throws UnsupportedOperationException * @see #getNumberOfBands() */ public void setBandLevel(short band, short level) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { int[] param = new int[2]; short[] value = new short[1]; param[0] = PARAM_BAND_LEVEL; param[1] = (int)band; value[0] = level; checkStatus(setParameter(param, value)); } /** * Gets the gain set for the given equalizer band. * @param band frequency band whose gain is requested. The numbering of the bands starts * from 0 and ends at (number of bands - 1). * @return the gain in millibels of the given band. * @throws IllegalStateException * @throws IllegalArgumentException * @throws UnsupportedOperationException */ public short getBandLevel(short band) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { int[] param = new int[2]; short[] result = new short[1]; param[0] = PARAM_BAND_LEVEL; param[1] = (int)band; checkStatus(getParameter(param, result)); return result[0]; } /** * Gets the center frequency of the given band. * @param band frequency band whose center frequency is requested. The numbering of the bands * starts from 0 and ends at (number of bands - 1). * @return the center frequency in milliHertz * @throws IllegalStateException * @throws IllegalArgumentException * @throws UnsupportedOperationException */ public int getCenterFreq(short band) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { int[] param = new int[2]; int[] result = new int[1]; param[0] = PARAM_CENTER_FREQ; param[1] = (int)band; checkStatus(getParameter(param, result)); return result[0]; } /** * Gets the frequency range of the given frequency band. * @param band frequency band whose frequency range is requested. The numbering of the bands * starts from 0 and ends at (number of bands - 1). * @return the frequency range in millHertz in an array of integers. The first element is the * lower limit of the range, the second element the upper limit. * @throws IllegalStateException * @throws IllegalArgumentException * @throws UnsupportedOperationException */ public int[] getBandFreqRange(short band) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { int[] param = new int[2]; int[] result = new int[2]; param[0] = PARAM_BAND_FREQ_RANGE; param[1] = (int)band; checkStatus(getParameter(param, result)); return result; } /** * Gets the band that has the most effect on the given frequency. * @param frequency frequency in milliHertz which is to be equalized via the returned band. * @return the frequency band that has most effect on the given frequency. * @throws IllegalStateException * @throws IllegalArgumentException * @throws UnsupportedOperationException */ public short getBand(int frequency) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { int[] param = new int[2]; short[] result = new short[1]; param[0] = PARAM_GET_BAND; param[1] = frequency; checkStatus(getParameter(param, result)); return result[0]; } /** * Gets current preset. * @return the preset that is set at the moment. * @throws IllegalStateException * @throws IllegalArgumentException * @throws UnsupportedOperationException */ public short getCurrentPreset() throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { short[] result = new short[1]; checkStatus(getParameter(PARAM_CURRENT_PRESET, result)); return result[0]; } /** * Sets the equalizer according to the given preset. * @param preset new preset that will be taken into use. The valid range is [0, * number of presets-1]. * @throws IllegalStateException * @throws IllegalArgumentException * @throws UnsupportedOperationException * @see #getNumberOfPresets() */ public void usePreset(short preset) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { checkStatus(setParameter(PARAM_CURRENT_PRESET, preset)); } /** * Gets the total number of presets the equalizer supports. The presets will have indices * [0, number of presets-1]. * @return the number of presets the equalizer supports. * @throws IllegalStateException * @throws IllegalArgumentException * @throws UnsupportedOperationException */ public short getNumberOfPresets() throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { short[] result = new short[1]; checkStatus(getParameter(PARAM_GET_NUM_OF_PRESETS, result)); return result[0]; } /** * Gets the preset name based on the index. * @param preset index of the preset. The valid range is [0, number of presets-1]. * @return a string containing the name of the given preset. * @throws IllegalStateException * @throws IllegalArgumentException * @throws UnsupportedOperationException */ public String getPresetName(short preset) { if (preset >= 0 && preset < mNumPresets) { return mPresetNames[preset]; } else { return ""; } } /** * The OnParameterChangeListener interface defines a method called by the Equalizer when a * parameter value has changed. */ public interface OnParameterChangeListener { /** * Method called when a parameter value has changed. The method is called only if the * parameter was changed by another application having the control of the same * Equalizer engine. * @param effect the Equalizer on which the interface is registered. * @param status status of the set parameter operation. * @param param1 ID of the modified parameter. See {@link #PARAM_BAND_LEVEL} ... * @param param2 additional parameter qualifier (e.g the band for band level parameter). * @param value the new parameter value. */ void onParameterChange(Equalizer effect, int status, int param1, int param2, int value); } /** * Listener used internally to receive unformatted parameter change events from AudioEffect * super class. */ private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { private BaseParameterListener() { } public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { OnParameterChangeListener l = null; synchronized (mParamListenerLock) { if (mParamListener != null) { l = mParamListener; } } if (l != null) { int p1 = -1; int p2 = -1; int v = -1; if (param.length >= 4) { p1 = byteArrayToInt(param, 0); if (param.length >= 8) { p2 = byteArrayToInt(param, 4); } } if (value.length == 2) { v = (int)byteArrayToShort(value, 0);; } else if (value.length == 4) { v = byteArrayToInt(value, 0); } if (p1 != -1 && v != -1) { l.onParameterChange(Equalizer.this, status, p1, p2, v); } } } } /** * Registers an OnParameterChangeListener interface. * @param listener OnParameterChangeListener interface registered */ public void setParameterListener(OnParameterChangeListener listener) { synchronized (mParamListenerLock) { if (mParamListener == null) { mParamListener = listener; mBaseParamListener = new BaseParameterListener(); super.setParameterListener(mBaseParamListener); } } } /** * The Settings class regroups all equalizer parameters. It is used in * conjuntion with getProperties() and setProperties() methods to backup and restore * all parameters in a single call. */ public static class Settings { public short curPreset; public short numBands = 0; public short[] bandLevels = null; public Settings() { } /** * Settings class constructor from a key=value; pairs formatted string. The string is * typically returned by Settings.toString() method. * @throws IllegalArgumentException if the string is not correctly formatted. */ public Settings(String settings) { StringTokenizer st = new StringTokenizer(settings, "=;"); int tokens = st.countTokens(); if (st.countTokens() < 5) { throw new IllegalArgumentException("settings: " + settings); } String key = st.nextToken(); if (!key.equals("Equalizer")) { throw new IllegalArgumentException( "invalid settings for Equalizer: " + key); } try { key = st.nextToken(); if (!key.equals("curPreset")) { throw new IllegalArgumentException("invalid key name: " + key); } curPreset = Short.parseShort(st.nextToken()); key = st.nextToken(); if (!key.equals("numBands")) { throw new IllegalArgumentException("invalid key name: " + key); } numBands = Short.parseShort(st.nextToken()); if (st.countTokens() != numBands*2) { throw new IllegalArgumentException("settings: " + settings); } bandLevels = new short[numBands]; for (int i = 0; i < numBands; i++) { key = st.nextToken(); if (!key.equals("band"+(i+1)+"Level")) { throw new IllegalArgumentException("invalid key name: " + key); } bandLevels[i] = Short.parseShort(st.nextToken()); } } catch (NumberFormatException nfe) { throw new IllegalArgumentException("invalid value for key: " + key); } } @Override public String toString() { String str = new String ( "Equalizer"+ ";curPreset="+Short.toString(curPreset)+ ";numBands="+Short.toString(numBands) ); for (int i = 0; i < numBands; i++) { str = str.concat(";band"+(i+1)+"Level="+Short.toString(bandLevels[i])); } return str; } }; /** * Gets the equalizer properties. This method is useful when a snapshot of current * equalizer settings must be saved by the application. * @return an Equalizer.Settings object containing all current parameters values * @throws IllegalStateException * @throws IllegalArgumentException * @throws UnsupportedOperationException */ public Equalizer.Settings getProperties() throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { byte[] param = new byte[4 + mNumBands * 2]; checkStatus(getParameter(PARAM_PROPERTIES, param)); Settings settings = new Settings(); settings.curPreset = byteArrayToShort(param, 0); settings.numBands = byteArrayToShort(param, 2); settings.bandLevels = new short[mNumBands]; for (int i = 0; i < mNumBands; i++) { settings.bandLevels[i] = byteArrayToShort(param, 4 + 2*i); } return settings; } /** * Sets the equalizer properties. This method is useful when equalizer settings have to * be applied from a previous backup. * @param settings an Equalizer.Settings object containing the properties to apply * @throws IllegalStateException * @throws IllegalArgumentException * @throws UnsupportedOperationException */ public void setProperties(Equalizer.Settings settings) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { if (settings.numBands != settings.bandLevels.length || settings.numBands != mNumBands) { throw new IllegalArgumentException("settings invalid band count: " +settings.numBands); } byte[] param = concatArrays(shortToByteArray(settings.curPreset), shortToByteArray(mNumBands)); for (int i = 0; i < mNumBands; i++) { param = concatArrays(param, shortToByteArray(settings.bandLevels[i])); } checkStatus(setParameter(PARAM_PROPERTIES, param)); } }