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