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