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