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