AudioEffect.java revision 1d154187a99d5ad8dd33d47173cf6a4456276e76
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 17package android.media.audiofx; 18 19import android.annotation.SdkConstant; 20import android.annotation.SdkConstant.SdkConstantType; 21import android.os.Handler; 22import android.os.Looper; 23import android.os.Message; 24import android.util.Log; 25import java.io.IOException; 26import java.lang.ref.WeakReference; 27import java.nio.ByteOrder; 28import java.nio.ByteBuffer; 29import java.util.UUID; 30 31/** 32 * AudioEffect is the base class for controlling audio effects provided by the android audio 33 * framework. 34 * <p>Applications should not use the AudioEffect class directly but one of its derived classes to 35 * control specific effects: 36 * <ul> 37 * <li> {@link android.media.audiofx.Equalizer}</li> 38 * <li> {@link android.media.audiofx.Virtualizer}</li> 39 * <li> {@link android.media.audiofx.BassBoost}</li> 40 * <li> {@link android.media.audiofx.PresetReverb}</li> 41 * <li> {@link android.media.audiofx.EnvironmentalReverb}</li> 42 * </ul> 43 * <p>To apply the audio effect to a specific AudioTrack or MediaPlayer instance, 44 * the application must specify the audio session ID of that instance when creating the AudioEffect. 45 * (see {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions). 46 * <p>NOTE: attaching insert effects (equalizer, bass boost, virtualizer) to the global audio output 47 * mix by use of session 0 is deprecated. 48 * <p>Creating an AudioEffect object will create the corresponding effect engine in the audio 49 * framework if no instance of the same effect type exists in the specified audio session. 50 * If one exists, this instance will be used. 51 * <p>The application creating the AudioEffect object (or a derived class) will either receive 52 * control of the effect engine or not depending on the priority parameter. If priority is higher 53 * than the priority used by the current effect engine owner, the control will be transfered to the 54 * new object. Otherwise control will remain with the previous object. In this case, the new 55 * application will be notified of changes in effect engine state or control ownership by the 56 * appropiate listener. 57 */ 58 59public class AudioEffect { 60 static { 61 System.loadLibrary("audioeffect_jni"); 62 native_init(); 63 } 64 65 private final static String TAG = "AudioEffect-JAVA"; 66 67 // effect type UUIDs are taken from hardware/libhardware/include/hardware/audio_effect.h 68 69 /** 70 * The following UUIDs define effect types corresponding to standard audio 71 * effects whose implementation and interface conform to the OpenSL ES 72 * specification. The definitions match the corresponding interface IDs in 73 * OpenSLES_IID.h 74 */ 75 /** 76 * UUID for environmental reverberation effect 77 */ 78 public static final UUID EFFECT_TYPE_ENV_REVERB = UUID 79 .fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e"); 80 /** 81 * UUID for preset reverberation effect 82 */ 83 public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID 84 .fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b"); 85 /** 86 * UUID for equalizer effect 87 */ 88 public static final UUID EFFECT_TYPE_EQUALIZER = UUID 89 .fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b"); 90 /** 91 * UUID for bass boost effect 92 */ 93 public static final UUID EFFECT_TYPE_BASS_BOOST = UUID 94 .fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b"); 95 /** 96 * UUID for virtualizer effect 97 */ 98 public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID 99 .fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b"); 100 101 /** 102 * UUIDs for effect types not covered by OpenSL ES. 103 */ 104 /** 105 * UUID for Automatic Gain Control (AGC) 106 */ 107 public static final UUID EFFECT_TYPE_AGC = UUID 108 .fromString("0a8abfe0-654c-11e0-ba26-0002a5d5c51b"); 109 110 /** 111 * UUID for Acoustic Echo Canceler (AEC) 112 */ 113 public static final UUID EFFECT_TYPE_AEC = UUID 114 .fromString("7b491460-8d4d-11e0-bd61-0002a5d5c51b"); 115 116 /** 117 * UUID for Noise Suppressor (NS) 118 */ 119 public static final UUID EFFECT_TYPE_NS = UUID 120 .fromString("58b4b260-8e06-11e0-aa8e-0002a5d5c51b"); 121 122 /** 123 * Null effect UUID. Used when the UUID for effect type of 124 * @hide 125 */ 126 public static final UUID EFFECT_TYPE_NULL = UUID 127 .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210"); 128 129 /** 130 * State of an AudioEffect object that was not successfully initialized upon 131 * creation 132 * @hide 133 */ 134 public static final int STATE_UNINITIALIZED = 0; 135 /** 136 * State of an AudioEffect object that is ready to be used. 137 * @hide 138 */ 139 public static final int STATE_INITIALIZED = 1; 140 141 // to keep in sync with 142 // frameworks/base/include/media/AudioEffect.h 143 /** 144 * Event id for engine control ownership change notification. 145 * @hide 146 */ 147 public static final int NATIVE_EVENT_CONTROL_STATUS = 0; 148 /** 149 * Event id for engine state change notification. 150 * @hide 151 */ 152 public static final int NATIVE_EVENT_ENABLED_STATUS = 1; 153 /** 154 * Event id for engine parameter change notification. 155 * @hide 156 */ 157 public static final int NATIVE_EVENT_PARAMETER_CHANGED = 2; 158 159 /** 160 * Successful operation. 161 */ 162 public static final int SUCCESS = 0; 163 /** 164 * Unspecified error. 165 */ 166 public static final int ERROR = -1; 167 /** 168 * Internal operation status. Not returned by any method. 169 */ 170 public static final int ALREADY_EXISTS = -2; 171 /** 172 * Operation failed due to bad object initialization. 173 */ 174 public static final int ERROR_NO_INIT = -3; 175 /** 176 * Operation failed due to bad parameter value. 177 */ 178 public static final int ERROR_BAD_VALUE = -4; 179 /** 180 * Operation failed because it was requested in wrong state. 181 */ 182 public static final int ERROR_INVALID_OPERATION = -5; 183 /** 184 * Operation failed due to lack of memory. 185 */ 186 public static final int ERROR_NO_MEMORY = -6; 187 /** 188 * Operation failed due to dead remote object. 189 */ 190 public static final int ERROR_DEAD_OBJECT = -7; 191 192 /** 193 * The effect descriptor contains information on a particular effect implemented in the 194 * audio framework:<br> 195 * <ul> 196 * <li>type: UUID identifying the effect type. May be one of: 197 * {@link AudioEffect#EFFECT_TYPE_AEC}, {@link AudioEffect#EFFECT_TYPE_AGC}, 198 * {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB}, 199 * {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS}, 200 * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}, {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}. 201 * </li> 202 * <li>uuid: UUID for this particular implementation</li> 203 * <li>connectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li> 204 * <li>name: human readable effect name</li> 205 * <li>implementor: human readable effect implementor name</li> 206 * </ul> 207 * The method {@link #queryEffects()} returns an array of Descriptors to facilitate effects 208 * enumeration. 209 */ 210 public static class Descriptor { 211 212 public Descriptor() { 213 } 214 215 /** 216 * @param type UUID identifying the effect type. May be one of: 217 * {@link AudioEffect#EFFECT_TYPE_AEC}, {@link AudioEffect#EFFECT_TYPE_AGC}, 218 * {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB}, 219 * {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS}, 220 * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}, 221 * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}. 222 * @param uuid UUID for this particular implementation 223 * @param connectMode {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY} 224 * @param name human readable effect name 225 * @param implementor human readable effect implementor name 226 * 227 */ 228 public Descriptor(String type, String uuid, String connectMode, 229 String name, String implementor) { 230 this.type = UUID.fromString(type); 231 this.uuid = UUID.fromString(uuid); 232 this.connectMode = connectMode; 233 this.name = name; 234 this.implementor = implementor; 235 } 236 237 /** 238 * Indicates the generic type of the effect (Equalizer, Bass boost ...). 239 * One of {@link AudioEffect#EFFECT_TYPE_AEC}, 240 * {@link AudioEffect#EFFECT_TYPE_AGC}, {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, 241 * {@link AudioEffect#EFFECT_TYPE_ENV_REVERB}, {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, 242 * {@link AudioEffect#EFFECT_TYPE_NS}, {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB} 243 * or {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}.<br> 244 * For reverberation, bass boost, EQ and virtualizer, the UUID 245 * corresponds to the OpenSL ES Interface ID. 246 */ 247 public UUID type; 248 /** 249 * Indicates the particular implementation of the effect in that type. Several effects 250 * can have the same type but this uuid is unique to a given implementation. 251 */ 252 public UUID uuid; 253 /** 254 * Indicates if the effect is of insert category {@link #EFFECT_INSERT} or auxiliary 255 * category {@link #EFFECT_AUXILIARY}. 256 * Insert effects (typically an {@link Equalizer}) are applied 257 * to the entire audio source and usually not shared by several sources. Auxiliary effects 258 * (typically a reverberator) are applied to part of the signal (wet) and the effect output 259 * is added to the original signal (dry). 260 * Audio pre processing are applied to audio captured on a particular 261 * {@link android.media.AudioRecord}. 262 */ 263 public String connectMode; 264 /** 265 * Human readable effect name 266 */ 267 public String name; 268 /** 269 * Human readable effect implementor name 270 */ 271 public String implementor; 272 }; 273 274 /** 275 * Effect connection mode is insert. Specifying an audio session ID when creating the effect 276 * will insert this effect after all players in the same audio session. 277 */ 278 public static final String EFFECT_INSERT = "Insert"; 279 /** 280 * Effect connection mode is auxiliary. 281 * <p>Auxiliary effects must be created on session 0 (global output mix). In order for a 282 * MediaPlayer or AudioTrack to be fed into this effect, they must be explicitely attached to 283 * this effect and a send level must be specified. 284 * <p>Use the effect ID returned by {@link #getId()} to designate this particular effect when 285 * attaching it to the MediaPlayer or AudioTrack. 286 */ 287 public static final String EFFECT_AUXILIARY = "Auxiliary"; 288 /** 289 * Effect connection mode is pre processing. 290 * The audio pre processing effects are attached to an audio input (AudioRecord). 291 * @hide 292 */ 293 public static final String EFFECT_PRE_PROCESSING = "Pre Processing"; 294 295 // -------------------------------------------------------------------------- 296 // Member variables 297 // -------------------- 298 /** 299 * Indicates the state of the AudioEffect instance 300 */ 301 private int mState = STATE_UNINITIALIZED; 302 /** 303 * Lock to synchronize access to mState 304 */ 305 private final Object mStateLock = new Object(); 306 /** 307 * System wide unique effect ID 308 */ 309 private int mId; 310 311 // accessed by native methods 312 private int mNativeAudioEffect; 313 private int mJniData; 314 315 /** 316 * Effect descriptor 317 */ 318 private Descriptor mDescriptor; 319 320 /** 321 * Listener for effect engine state change notifications. 322 * 323 * @see #setEnableStatusListener(OnEnableStatusChangeListener) 324 */ 325 private OnEnableStatusChangeListener mEnableStatusChangeListener = null; 326 /** 327 * Listener for effect engine control ownership change notifications. 328 * 329 * @see #setControlStatusListener(OnControlStatusChangeListener) 330 */ 331 private OnControlStatusChangeListener mControlChangeStatusListener = null; 332 /** 333 * Listener for effect engine control ownership change notifications. 334 * 335 * @see #setParameterListener(OnParameterChangeListener) 336 */ 337 private OnParameterChangeListener mParameterChangeListener = null; 338 /** 339 * Lock to protect listeners updates against event notifications 340 * @hide 341 */ 342 public final Object mListenerLock = new Object(); 343 /** 344 * Handler for events coming from the native code 345 * @hide 346 */ 347 public NativeEventHandler mNativeEventHandler = null; 348 349 // -------------------------------------------------------------------------- 350 // Constructor, Finalize 351 // -------------------- 352 /** 353 * Class constructor. 354 * 355 * @param type type of effect engine created. See {@link #EFFECT_TYPE_ENV_REVERB}, 356 * {@link #EFFECT_TYPE_EQUALIZER} ... Types corresponding to 357 * built-in effects are defined by AudioEffect class. Other types 358 * can be specified provided they correspond an existing OpenSL 359 * ES interface ID and the corresponsing effect is available on 360 * the platform. If an unspecified effect type is requested, the 361 * constructor with throw the IllegalArgumentException. This 362 * parameter can be set to {@link #EFFECT_TYPE_NULL} in which 363 * case only the uuid will be used to select the effect. 364 * @param uuid unique identifier of a particular effect implementation. 365 * Must be specified if the caller wants to use a particular 366 * implementation of an effect type. This parameter can be set to 367 * {@link #EFFECT_TYPE_NULL} in which case only the type will 368 * be used to select the effect. 369 * @param priority the priority level requested by the application for 370 * controlling the effect engine. As the same effect engine can 371 * be shared by several applications, this parameter indicates 372 * how much the requesting application needs control of effect 373 * parameters. The normal priority is 0, above normal is a 374 * positive number, below normal a negative number. 375 * @param audioSession system wide unique audio session identifier. 376 * The effect will be attached to the MediaPlayer or AudioTrack in 377 * the same audio session. 378 * 379 * @throws java.lang.IllegalArgumentException 380 * @throws java.lang.UnsupportedOperationException 381 * @throws java.lang.RuntimeException 382 * @hide 383 */ 384 385 public AudioEffect(UUID type, UUID uuid, int priority, int audioSession) 386 throws IllegalArgumentException, UnsupportedOperationException, 387 RuntimeException { 388 int[] id = new int[1]; 389 Descriptor[] desc = new Descriptor[1]; 390 // native initialization 391 int initResult = native_setup(new WeakReference<AudioEffect>(this), 392 type.toString(), uuid.toString(), priority, audioSession, id, 393 desc); 394 if (initResult != SUCCESS && initResult != ALREADY_EXISTS) { 395 Log.e(TAG, "Error code " + initResult 396 + " when initializing AudioEffect."); 397 switch (initResult) { 398 case ERROR_BAD_VALUE: 399 throw (new IllegalArgumentException("Effect type: " + type 400 + " not supported.")); 401 case ERROR_INVALID_OPERATION: 402 throw (new UnsupportedOperationException( 403 "Effect library not loaded")); 404 default: 405 throw (new RuntimeException( 406 "Cannot initialize effect engine for type: " + type 407 + " Error: " + initResult)); 408 } 409 } 410 mId = id[0]; 411 mDescriptor = desc[0]; 412 synchronized (mStateLock) { 413 mState = STATE_INITIALIZED; 414 } 415 } 416 417 /** 418 * Releases the native AudioEffect resources. It is a good practice to 419 * release the effect engine when not in use as control can be returned to 420 * other applications or the native resources released. 421 */ 422 public void release() { 423 synchronized (mStateLock) { 424 native_release(); 425 mState = STATE_UNINITIALIZED; 426 } 427 } 428 429 @Override 430 protected void finalize() { 431 native_finalize(); 432 } 433 434 /** 435 * Get the effect descriptor. 436 * 437 * @see android.media.audiofx.AudioEffect.Descriptor 438 * @throws IllegalStateException 439 */ 440 public Descriptor getDescriptor() throws IllegalStateException { 441 checkState("getDescriptor()"); 442 return mDescriptor; 443 } 444 445 // -------------------------------------------------------------------------- 446 // Effects Enumeration 447 // -------------------- 448 449 /** 450 * Query all effects available on the platform. Returns an array of 451 * {@link android.media.audiofx.AudioEffect.Descriptor} objects 452 * 453 * @throws IllegalStateException 454 */ 455 456 static public Descriptor[] queryEffects() { 457 return (Descriptor[]) native_query_effects(); 458 } 459 460 /** 461 * Query all audio pre-processing effects applied to the AudioRecord with the supplied 462 * audio session ID. Returns an array of {@link android.media.audiofx.AudioEffect.Descriptor} 463 * objects. 464 * @param audioSession system wide unique audio session identifier. 465 * @throws IllegalStateException 466 * @hide 467 */ 468 469 static public Descriptor[] queryPreProcessings(int audioSession) { 470 return (Descriptor[]) native_query_pre_processing(audioSession); 471 } 472 473 /** 474 * Checks if the device implements the specified effect type. 475 * @param type the requested effect type. 476 * @return true if the device implements the specified effect type, false otherwise. 477 * @hide 478 */ 479 public static boolean isEffectTypeAvailable(UUID type) { 480 AudioEffect.Descriptor[] desc = AudioEffect.queryEffects(); 481 for (int i = 0; i < desc.length; i++) { 482 if (desc[i].type.equals(type)) { 483 return true; 484 } 485 } 486 return false; 487 } 488 489 // -------------------------------------------------------------------------- 490 // Control methods 491 // -------------------- 492 493 /** 494 * Enable or disable the effect. 495 * Creating an audio effect does not automatically apply this effect on the audio source. It 496 * creates the resources necessary to process this effect but the audio signal is still bypassed 497 * through the effect engine. Calling this method will make that the effect is actually applied 498 * or not to the audio content being played in the corresponding audio session. 499 * 500 * @param enabled the requested enable state 501 * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION} 502 * or {@link #ERROR_DEAD_OBJECT} in case of failure. 503 * @throws IllegalStateException 504 */ 505 public int setEnabled(boolean enabled) throws IllegalStateException { 506 checkState("setEnabled()"); 507 return native_setEnabled(enabled); 508 } 509 510 /** 511 * Set effect parameter. The setParameter method is provided in several 512 * forms addressing most common parameter formats. This form is the most 513 * generic one where the parameter and its value are both specified as an 514 * array of bytes. The parameter and value type and length are therefore 515 * totally free. For standard effect defined by OpenSL ES, the parameter 516 * format and values must match the definitions in the corresponding OpenSL 517 * ES interface. 518 * 519 * @param param the identifier of the parameter to set 520 * @param value the new value for the specified parameter 521 * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE}, 522 * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or 523 * {@link #ERROR_DEAD_OBJECT} in case of failure 524 * @throws IllegalStateException 525 * @hide 526 */ 527 public int setParameter(byte[] param, byte[] value) 528 throws IllegalStateException { 529 checkState("setParameter()"); 530 return native_setParameter(param.length, param, value.length, value); 531 } 532 533 /** 534 * Set effect parameter. The parameter and its value are integers. 535 * 536 * @see #setParameter(byte[], byte[]) 537 * @hide 538 */ 539 public int setParameter(int param, int value) throws IllegalStateException { 540 byte[] p = intToByteArray(param); 541 byte[] v = intToByteArray(value); 542 return setParameter(p, v); 543 } 544 545 /** 546 * Set effect parameter. The parameter is an integer and the value is a 547 * short integer. 548 * 549 * @see #setParameter(byte[], byte[]) 550 * @hide 551 */ 552 public int setParameter(int param, short value) 553 throws IllegalStateException { 554 byte[] p = intToByteArray(param); 555 byte[] v = shortToByteArray(value); 556 return setParameter(p, v); 557 } 558 559 /** 560 * Set effect parameter. The parameter is an integer and the value is an 561 * array of bytes. 562 * 563 * @see #setParameter(byte[], byte[]) 564 * @hide 565 */ 566 public int setParameter(int param, byte[] value) 567 throws IllegalStateException { 568 byte[] p = intToByteArray(param); 569 return setParameter(p, value); 570 } 571 572 /** 573 * Set effect parameter. The parameter is an array of 1 or 2 integers and 574 * the value is also an array of 1 or 2 integers 575 * 576 * @see #setParameter(byte[], byte[]) 577 * @hide 578 */ 579 public int setParameter(int[] param, int[] value) 580 throws IllegalStateException { 581 if (param.length > 2 || value.length > 2) { 582 return ERROR_BAD_VALUE; 583 } 584 byte[] p = intToByteArray(param[0]); 585 if (param.length > 1) { 586 byte[] p2 = intToByteArray(param[1]); 587 p = concatArrays(p, p2); 588 } 589 byte[] v = intToByteArray(value[0]); 590 if (value.length > 1) { 591 byte[] v2 = intToByteArray(value[1]); 592 v = concatArrays(v, v2); 593 } 594 return setParameter(p, v); 595 } 596 597 /** 598 * Set effect parameter. The parameter is an array of 1 or 2 integers and 599 * the value is an array of 1 or 2 short integers 600 * 601 * @see #setParameter(byte[], byte[]) 602 * @hide 603 */ 604 public int setParameter(int[] param, short[] value) 605 throws IllegalStateException { 606 if (param.length > 2 || value.length > 2) { 607 return ERROR_BAD_VALUE; 608 } 609 byte[] p = intToByteArray(param[0]); 610 if (param.length > 1) { 611 byte[] p2 = intToByteArray(param[1]); 612 p = concatArrays(p, p2); 613 } 614 615 byte[] v = shortToByteArray(value[0]); 616 if (value.length > 1) { 617 byte[] v2 = shortToByteArray(value[1]); 618 v = concatArrays(v, v2); 619 } 620 return setParameter(p, v); 621 } 622 623 /** 624 * Set effect parameter. The parameter is an array of 1 or 2 integers and 625 * the value is an array of bytes 626 * 627 * @see #setParameter(byte[], byte[]) 628 * @hide 629 */ 630 public int setParameter(int[] param, byte[] value) 631 throws IllegalStateException { 632 if (param.length > 2) { 633 return ERROR_BAD_VALUE; 634 } 635 byte[] p = intToByteArray(param[0]); 636 if (param.length > 1) { 637 byte[] p2 = intToByteArray(param[1]); 638 p = concatArrays(p, p2); 639 } 640 return setParameter(p, value); 641 } 642 643 /** 644 * Get effect parameter. The getParameter method is provided in several 645 * forms addressing most common parameter formats. This form is the most 646 * generic one where the parameter and its value are both specified as an 647 * array of bytes. The parameter and value type and length are therefore 648 * totally free. 649 * 650 * @param param the identifier of the parameter to set 651 * @param value the new value for the specified parameter 652 * @return the number of meaningful bytes in value array in case of success or 653 * {@link #ERROR_BAD_VALUE}, {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} 654 * or {@link #ERROR_DEAD_OBJECT} in case of failure. 655 * @throws IllegalStateException 656 * @hide 657 */ 658 public int getParameter(byte[] param, byte[] value) 659 throws IllegalStateException { 660 checkState("getParameter()"); 661 return native_getParameter(param.length, param, value.length, value); 662 } 663 664 /** 665 * Get effect parameter. The parameter is an integer and the value is an 666 * array of bytes. 667 * 668 * @see #getParameter(byte[], byte[]) 669 * @hide 670 */ 671 public int getParameter(int param, byte[] value) 672 throws IllegalStateException { 673 byte[] p = intToByteArray(param); 674 675 return getParameter(p, value); 676 } 677 678 /** 679 * Get effect parameter. The parameter is an integer and the value is an 680 * array of 1 or 2 integers 681 * 682 * @see #getParameter(byte[], byte[]) 683 * In case of success, returns the number of meaningful integers in value array. 684 * @hide 685 */ 686 public int getParameter(int param, int[] value) 687 throws IllegalStateException { 688 if (value.length > 2) { 689 return ERROR_BAD_VALUE; 690 } 691 byte[] p = intToByteArray(param); 692 693 byte[] v = new byte[value.length * 4]; 694 695 int status = getParameter(p, v); 696 697 if (status == 4 || status == 8) { 698 value[0] = byteArrayToInt(v); 699 if (status == 8) { 700 value[1] = byteArrayToInt(v, 4); 701 } 702 status /= 4; 703 } else { 704 status = ERROR; 705 } 706 return status; 707 } 708 709 /** 710 * Get effect parameter. The parameter is an integer and the value is an 711 * array of 1 or 2 short integers 712 * 713 * @see #getParameter(byte[], byte[]) 714 * In case of success, returns the number of meaningful short integers in value array. 715 * @hide 716 */ 717 public int getParameter(int param, short[] value) 718 throws IllegalStateException { 719 if (value.length > 2) { 720 return ERROR_BAD_VALUE; 721 } 722 byte[] p = intToByteArray(param); 723 724 byte[] v = new byte[value.length * 2]; 725 726 int status = getParameter(p, v); 727 728 if (status == 2 || status == 4) { 729 value[0] = byteArrayToShort(v); 730 if (status == 4) { 731 value[1] = byteArrayToShort(v, 2); 732 } 733 status /= 2; 734 } else { 735 status = ERROR; 736 } 737 return status; 738 } 739 740 /** 741 * Get effect parameter. The parameter is an array of 1 or 2 integers and 742 * the value is also an array of 1 or 2 integers 743 * 744 * @see #getParameter(byte[], byte[]) 745 * In case of success, the returns the number of meaningful integers in value array. 746 * @hide 747 */ 748 public int getParameter(int[] param, int[] value) 749 throws IllegalStateException { 750 if (param.length > 2 || value.length > 2) { 751 return ERROR_BAD_VALUE; 752 } 753 byte[] p = intToByteArray(param[0]); 754 if (param.length > 1) { 755 byte[] p2 = intToByteArray(param[1]); 756 p = concatArrays(p, p2); 757 } 758 byte[] v = new byte[value.length * 4]; 759 760 int status = getParameter(p, v); 761 762 if (status == 4 || status == 8) { 763 value[0] = byteArrayToInt(v); 764 if (status == 8) { 765 value[1] = byteArrayToInt(v, 4); 766 } 767 status /= 4; 768 } else { 769 status = ERROR; 770 } 771 return status; 772 } 773 774 /** 775 * Get effect parameter. The parameter is an array of 1 or 2 integers and 776 * the value is an array of 1 or 2 short integers 777 * 778 * @see #getParameter(byte[], byte[]) 779 * In case of success, returns the number of meaningful short integers in value array. 780 * @hide 781 */ 782 public int getParameter(int[] param, short[] value) 783 throws IllegalStateException { 784 if (param.length > 2 || value.length > 2) { 785 return ERROR_BAD_VALUE; 786 } 787 byte[] p = intToByteArray(param[0]); 788 if (param.length > 1) { 789 byte[] p2 = intToByteArray(param[1]); 790 p = concatArrays(p, p2); 791 } 792 byte[] v = new byte[value.length * 2]; 793 794 int status = getParameter(p, v); 795 796 if (status == 2 || status == 4) { 797 value[0] = byteArrayToShort(v); 798 if (status == 4) { 799 value[1] = byteArrayToShort(v, 2); 800 } 801 status /= 2; 802 } else { 803 status = ERROR; 804 } 805 return status; 806 } 807 808 /** 809 * Get effect parameter. The parameter is an array of 1 or 2 integers and 810 * the value is an array of bytes 811 * 812 * @see #getParameter(byte[], byte[]) 813 * @hide 814 */ 815 public int getParameter(int[] param, byte[] value) 816 throws IllegalStateException { 817 if (param.length > 2) { 818 return ERROR_BAD_VALUE; 819 } 820 byte[] p = intToByteArray(param[0]); 821 if (param.length > 1) { 822 byte[] p2 = intToByteArray(param[1]); 823 p = concatArrays(p, p2); 824 } 825 826 return getParameter(p, value); 827 } 828 829 /** 830 * Send a command to the effect engine. This method is intended to send 831 * proprietary commands to a particular effect implementation. 832 * In case of success, returns the number of meaningful bytes in reply array. 833 * In case of failure, the returned value is negative and implementation specific. 834 * @hide 835 */ 836 public int command(int cmdCode, byte[] command, byte[] reply) 837 throws IllegalStateException { 838 checkState("command()"); 839 return native_command(cmdCode, command.length, command, reply.length, reply); 840 } 841 842 // -------------------------------------------------------------------------- 843 // Getters 844 // -------------------- 845 846 /** 847 * Returns effect unique identifier. This system wide unique identifier can 848 * be used to attach this effect to a MediaPlayer or an AudioTrack when the 849 * effect is an auxiliary effect (Reverb) 850 * 851 * @return the effect identifier. 852 * @throws IllegalStateException 853 */ 854 public int getId() throws IllegalStateException { 855 checkState("getId()"); 856 return mId; 857 } 858 859 /** 860 * Returns effect enabled state 861 * 862 * @return true if the effect is enabled, false otherwise. 863 * @throws IllegalStateException 864 */ 865 public boolean getEnabled() throws IllegalStateException { 866 checkState("getEnabled()"); 867 return native_getEnabled(); 868 } 869 870 /** 871 * Checks if this AudioEffect object is controlling the effect engine. 872 * 873 * @return true if this instance has control of effect engine, false 874 * otherwise. 875 * @throws IllegalStateException 876 */ 877 public boolean hasControl() throws IllegalStateException { 878 checkState("hasControl()"); 879 return native_hasControl(); 880 } 881 882 // -------------------------------------------------------------------------- 883 // Initialization / configuration 884 // -------------------- 885 /** 886 * Sets the listener AudioEffect notifies when the effect engine is enabled 887 * or disabled. 888 * 889 * @param listener 890 */ 891 public void setEnableStatusListener(OnEnableStatusChangeListener listener) { 892 synchronized (mListenerLock) { 893 mEnableStatusChangeListener = listener; 894 } 895 if ((listener != null) && (mNativeEventHandler == null)) { 896 createNativeEventHandler(); 897 } 898 } 899 900 /** 901 * Sets the listener AudioEffect notifies when the effect engine control is 902 * taken or returned. 903 * 904 * @param listener 905 */ 906 public void setControlStatusListener(OnControlStatusChangeListener listener) { 907 synchronized (mListenerLock) { 908 mControlChangeStatusListener = listener; 909 } 910 if ((listener != null) && (mNativeEventHandler == null)) { 911 createNativeEventHandler(); 912 } 913 } 914 915 /** 916 * Sets the listener AudioEffect notifies when a parameter is changed. 917 * 918 * @param listener 919 * @hide 920 */ 921 public void setParameterListener(OnParameterChangeListener listener) { 922 synchronized (mListenerLock) { 923 mParameterChangeListener = listener; 924 } 925 if ((listener != null) && (mNativeEventHandler == null)) { 926 createNativeEventHandler(); 927 } 928 } 929 930 // Convenience method for the creation of the native event handler 931 // It is called only when a non-null event listener is set. 932 // precondition: 933 // mNativeEventHandler is null 934 private void createNativeEventHandler() { 935 Looper looper; 936 if ((looper = Looper.myLooper()) != null) { 937 mNativeEventHandler = new NativeEventHandler(this, looper); 938 } else if ((looper = Looper.getMainLooper()) != null) { 939 mNativeEventHandler = new NativeEventHandler(this, looper); 940 } else { 941 mNativeEventHandler = null; 942 } 943 } 944 945 // --------------------------------------------------------- 946 // Interface definitions 947 // -------------------- 948 /** 949 * The OnEnableStatusChangeListener interface defines a method called by the AudioEffect 950 * when a the enabled state of the effect engine was changed by the controlling application. 951 */ 952 public interface OnEnableStatusChangeListener { 953 /** 954 * Called on the listener to notify it that the effect engine has been 955 * enabled or disabled. 956 * @param effect the effect on which the interface is registered. 957 * @param enabled new effect state. 958 */ 959 void onEnableStatusChange(AudioEffect effect, boolean enabled); 960 } 961 962 /** 963 * The OnControlStatusChangeListener interface defines a method called by the AudioEffect 964 * when a the control of the effect engine is gained or lost by the application 965 */ 966 public interface OnControlStatusChangeListener { 967 /** 968 * Called on the listener to notify it that the effect engine control 969 * has been taken or returned. 970 * @param effect the effect on which the interface is registered. 971 * @param controlGranted true if the application has been granted control of the effect 972 * engine, false otherwise. 973 */ 974 void onControlStatusChange(AudioEffect effect, boolean controlGranted); 975 } 976 977 /** 978 * The OnParameterChangeListener interface defines a method called by the AudioEffect 979 * when a parameter is changed in the effect engine by the controlling application. 980 * @hide 981 */ 982 public interface OnParameterChangeListener { 983 /** 984 * Called on the listener to notify it that a parameter value has changed. 985 * @param effect the effect on which the interface is registered. 986 * @param status status of the set parameter operation. 987 * @param param ID of the modified parameter. 988 * @param value the new parameter value. 989 */ 990 void onParameterChange(AudioEffect effect, int status, byte[] param, 991 byte[] value); 992 } 993 994 995 // ------------------------------------------------------------------------- 996 // Audio Effect Control panel intents 997 // ------------------------------------------------------------------------- 998 999 /** 1000 * Intent to launch an audio effect control panel UI. 1001 * <p>The goal of this intent is to enable separate implementations of music/media player 1002 * applications and audio effect control application or services. 1003 * This will allow platform vendors to offer more advanced control options for standard effects 1004 * or control for platform specific effects. 1005 * <p>The intent carries a number of extras used by the player application to communicate 1006 * necessary pieces of information to the control panel application. 1007 * <p>The calling application must use the 1008 * {@link android.app.Activity#startActivityForResult(Intent, int)} method to launch the 1009 * control panel so that its package name is indicated and used by the control panel 1010 * application to keep track of changes for this particular application. 1011 * <p>The {@link #EXTRA_AUDIO_SESSION} extra will indicate an audio session to which the 1012 * audio effects should be applied. If no audio session is specified, either one of the 1013 * follownig will happen: 1014 * <p>- If an audio session was previously opened by the calling application with 1015 * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intent, the effect changes will 1016 * be applied to that session. 1017 * <p>- If no audio session is opened, the changes will be stored in the package specific 1018 * storage area and applied whenever a new audio session is opened by this application. 1019 * <p>The {@link #EXTRA_CONTENT_TYPE} extra will help the control panel application 1020 * customize both the UI layout and the default audio effect settings if none are already 1021 * stored for the calling application. 1022 */ 1023 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 1024 public static final String ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL = 1025 "android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL"; 1026 1027 /** 1028 * Intent to signal to the effect control application or service that a new audio session 1029 * is opened and requires audio effects to be applied. 1030 * <p>This is different from {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} in that no 1031 * UI should be displayed in this case. Music player applications can broadcast this intent 1032 * before starting playback to make sure that any audio effect settings previously selected 1033 * by the user are applied. 1034 * <p>The effect control application receiving this intent will look for previously stored 1035 * settings for the calling application, create all required audio effects and apply the 1036 * effect settings to the specified audio session. 1037 * <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the 1038 * audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory. 1039 * <p>If no stored settings are found for the calling application, default settings for the 1040 * content type indicated by {@link #EXTRA_CONTENT_TYPE} will be applied. The default settings 1041 * for a given content type are platform specific. 1042 */ 1043 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 1044 public static final String ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION = 1045 "android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION"; 1046 1047 /** 1048 * Intent to signal to the effect control application or service that an audio session 1049 * is closed and that effects should not be applied anymore. 1050 * <p>The effect control application receiving this intent will delete all effects on 1051 * this session and store current settings in package specific storage. 1052 * <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the 1053 * audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory. 1054 * <p>It is good practice for applications to broadcast this intent when music playback stops 1055 * and/or when exiting to free system resources consumed by audio effect engines. 1056 */ 1057 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 1058 public static final String ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION = 1059 "android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION"; 1060 1061 /** 1062 * Contains the ID of the audio session the effects should be applied to. 1063 * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL}, 1064 * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and 1065 * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents. 1066 * <p>The extra value is of type int and is the audio session ID. 1067 * @see android.media.MediaPlayer#getAudioSessionId() for details on audio sessions. 1068 */ 1069 public static final String EXTRA_AUDIO_SESSION = "android.media.extra.AUDIO_SESSION"; 1070 1071 /** 1072 * Contains the package name of the calling application. 1073 * <p>This extra is for use with {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and 1074 * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents. 1075 * <p>The extra value is a string containing the full package name. 1076 */ 1077 public static final String EXTRA_PACKAGE_NAME = "android.media.extra.PACKAGE_NAME"; 1078 1079 /** 1080 * Indicates which type of content is played by the application. 1081 * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} and 1082 * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intents. 1083 * <p>This information is used by the effect control application to customize UI and select 1084 * appropriate default effect settings. The content type is one of the following: 1085 * <ul> 1086 * <li>{@link #CONTENT_TYPE_MUSIC}</li> 1087 * <li>{@link #CONTENT_TYPE_MOVIE}</li> 1088 * <li>{@link #CONTENT_TYPE_GAME}</li> 1089 * <li>{@link #CONTENT_TYPE_VOICE}</li> 1090 * </ul> 1091 * If omitted, the content type defaults to {@link #CONTENT_TYPE_MUSIC}. 1092 */ 1093 public static final String EXTRA_CONTENT_TYPE = "android.media.extra.CONTENT_TYPE"; 1094 1095 /** 1096 * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is music 1097 */ 1098 public static final int CONTENT_TYPE_MUSIC = 0; 1099 /** 1100 * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is video or movie 1101 */ 1102 public static final int CONTENT_TYPE_MOVIE = 1; 1103 /** 1104 * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is game audio 1105 */ 1106 public static final int CONTENT_TYPE_GAME = 2; 1107 /** 1108 * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is voice audio 1109 */ 1110 public static final int CONTENT_TYPE_VOICE = 3; 1111 1112 1113 // --------------------------------------------------------- 1114 // Inner classes 1115 // -------------------- 1116 /** 1117 * Helper class to handle the forwarding of native events to the appropriate 1118 * listeners 1119 */ 1120 private class NativeEventHandler extends Handler { 1121 private AudioEffect mAudioEffect; 1122 1123 public NativeEventHandler(AudioEffect ae, Looper looper) { 1124 super(looper); 1125 mAudioEffect = ae; 1126 } 1127 1128 @Override 1129 public void handleMessage(Message msg) { 1130 if (mAudioEffect == null) { 1131 return; 1132 } 1133 switch (msg.what) { 1134 case NATIVE_EVENT_ENABLED_STATUS: 1135 OnEnableStatusChangeListener enableStatusChangeListener = null; 1136 synchronized (mListenerLock) { 1137 enableStatusChangeListener = mAudioEffect.mEnableStatusChangeListener; 1138 } 1139 if (enableStatusChangeListener != null) { 1140 enableStatusChangeListener.onEnableStatusChange( 1141 mAudioEffect, (boolean) (msg.arg1 != 0)); 1142 } 1143 break; 1144 case NATIVE_EVENT_CONTROL_STATUS: 1145 OnControlStatusChangeListener controlStatusChangeListener = null; 1146 synchronized (mListenerLock) { 1147 controlStatusChangeListener = mAudioEffect.mControlChangeStatusListener; 1148 } 1149 if (controlStatusChangeListener != null) { 1150 controlStatusChangeListener.onControlStatusChange( 1151 mAudioEffect, (boolean) (msg.arg1 != 0)); 1152 } 1153 break; 1154 case NATIVE_EVENT_PARAMETER_CHANGED: 1155 OnParameterChangeListener parameterChangeListener = null; 1156 synchronized (mListenerLock) { 1157 parameterChangeListener = mAudioEffect.mParameterChangeListener; 1158 } 1159 if (parameterChangeListener != null) { 1160 // arg1 contains offset of parameter value from start of 1161 // byte array 1162 int vOffset = msg.arg1; 1163 byte[] p = (byte[]) msg.obj; 1164 // See effect_param_t in EffectApi.h for psize and vsize 1165 // fields offsets 1166 int status = byteArrayToInt(p, 0); 1167 int psize = byteArrayToInt(p, 4); 1168 int vsize = byteArrayToInt(p, 8); 1169 byte[] param = new byte[psize]; 1170 byte[] value = new byte[vsize]; 1171 System.arraycopy(p, 12, param, 0, psize); 1172 System.arraycopy(p, vOffset, value, 0, vsize); 1173 1174 parameterChangeListener.onParameterChange(mAudioEffect, 1175 status, param, value); 1176 } 1177 break; 1178 1179 default: 1180 Log.e(TAG, "handleMessage() Unknown event type: " + msg.what); 1181 break; 1182 } 1183 } 1184 } 1185 1186 // --------------------------------------------------------- 1187 // Java methods called from the native side 1188 // -------------------- 1189 @SuppressWarnings("unused") 1190 private static void postEventFromNative(Object effect_ref, int what, 1191 int arg1, int arg2, Object obj) { 1192 AudioEffect effect = (AudioEffect) ((WeakReference) effect_ref).get(); 1193 if (effect == null) { 1194 return; 1195 } 1196 if (effect.mNativeEventHandler != null) { 1197 Message m = effect.mNativeEventHandler.obtainMessage(what, arg1, 1198 arg2, obj); 1199 effect.mNativeEventHandler.sendMessage(m); 1200 } 1201 1202 } 1203 1204 // --------------------------------------------------------- 1205 // Native methods called from the Java side 1206 // -------------------- 1207 1208 private static native final void native_init(); 1209 1210 private native final int native_setup(Object audioeffect_this, String type, 1211 String uuid, int priority, int audioSession, int[] id, Object[] desc); 1212 1213 private native final void native_finalize(); 1214 1215 private native final void native_release(); 1216 1217 private native final int native_setEnabled(boolean enabled); 1218 1219 private native final boolean native_getEnabled(); 1220 1221 private native final boolean native_hasControl(); 1222 1223 private native final int native_setParameter(int psize, byte[] param, 1224 int vsize, byte[] value); 1225 1226 private native final int native_getParameter(int psize, byte[] param, 1227 int vsize, byte[] value); 1228 1229 private native final int native_command(int cmdCode, int cmdSize, 1230 byte[] cmdData, int repSize, byte[] repData); 1231 1232 private static native Object[] native_query_effects(); 1233 1234 private static native Object[] native_query_pre_processing(int audioSession); 1235 1236 // --------------------------------------------------------- 1237 // Utility methods 1238 // ------------------ 1239 1240 /** 1241 * @hide 1242 */ 1243 public void checkState(String methodName) throws IllegalStateException { 1244 synchronized (mStateLock) { 1245 if (mState != STATE_INITIALIZED) { 1246 throw (new IllegalStateException(methodName 1247 + " called on uninitialized AudioEffect.")); 1248 } 1249 } 1250 } 1251 1252 /** 1253 * @hide 1254 */ 1255 public void checkStatus(int status) { 1256 if (isError(status)) { 1257 switch (status) { 1258 case AudioEffect.ERROR_BAD_VALUE: 1259 throw (new IllegalArgumentException( 1260 "AudioEffect: bad parameter value")); 1261 case AudioEffect.ERROR_INVALID_OPERATION: 1262 throw (new UnsupportedOperationException( 1263 "AudioEffect: invalid parameter operation")); 1264 default: 1265 throw (new RuntimeException("AudioEffect: set/get parameter error")); 1266 } 1267 } 1268 } 1269 1270 /** 1271 * @hide 1272 */ 1273 public static boolean isError(int status) { 1274 return (status < 0); 1275 } 1276 1277 /** 1278 * @hide 1279 */ 1280 public int byteArrayToInt(byte[] valueBuf) { 1281 return byteArrayToInt(valueBuf, 0); 1282 1283 } 1284 1285 /** 1286 * @hide 1287 */ 1288 public int byteArrayToInt(byte[] valueBuf, int offset) { 1289 ByteBuffer converter = ByteBuffer.wrap(valueBuf); 1290 converter.order(ByteOrder.nativeOrder()); 1291 return converter.getInt(offset); 1292 1293 } 1294 1295 /** 1296 * @hide 1297 */ 1298 public byte[] intToByteArray(int value) { 1299 ByteBuffer converter = ByteBuffer.allocate(4); 1300 converter.order(ByteOrder.nativeOrder()); 1301 converter.putInt(value); 1302 return converter.array(); 1303 } 1304 1305 /** 1306 * @hide 1307 */ 1308 public short byteArrayToShort(byte[] valueBuf) { 1309 return byteArrayToShort(valueBuf, 0); 1310 } 1311 1312 /** 1313 * @hide 1314 */ 1315 public short byteArrayToShort(byte[] valueBuf, int offset) { 1316 ByteBuffer converter = ByteBuffer.wrap(valueBuf); 1317 converter.order(ByteOrder.nativeOrder()); 1318 return converter.getShort(offset); 1319 1320 } 1321 1322 /** 1323 * @hide 1324 */ 1325 public byte[] shortToByteArray(short value) { 1326 ByteBuffer converter = ByteBuffer.allocate(2); 1327 converter.order(ByteOrder.nativeOrder()); 1328 short sValue = (short) value; 1329 converter.putShort(sValue); 1330 return converter.array(); 1331 } 1332 1333 /** 1334 * @hide 1335 */ 1336 public byte[] concatArrays(byte[]... arrays) { 1337 int len = 0; 1338 for (byte[] a : arrays) { 1339 len += a.length; 1340 } 1341 byte[] b = new byte[len]; 1342 1343 int offs = 0; 1344 for (byte[] a : arrays) { 1345 System.arraycopy(a, 0, b, offs, a.length); 1346 offs += a.length; 1347 } 1348 return b; 1349 } 1350} 1351