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>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 61 public 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 opreation 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 {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE}, 577 * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or 578 * {@link #ERROR_DEAD_OBJECT} in case of failure When called, value.length 579 * indicates the maximum size of the returned parameters value. When 580 * returning, value.length is updated with the actual size of the 581 * returned value. 582 * @throws IllegalStateException 583 * @hide 584 */ 585 public int getParameter(byte[] param, byte[] value) 586 throws IllegalStateException { 587 checkState("getParameter()"); 588 int[] vSize = new int[1]; 589 vSize[0] = value.length; 590 int status = native_getParameter(param.length, param, vSize, value); 591 if (value.length > vSize[0]) { 592 byte[] resizedValue = new byte[vSize[0]]; 593 System.arraycopy(value, 0, resizedValue, 0, vSize[0]); 594 value = resizedValue; 595 } 596 return status; 597 } 598 599 /** 600 * Get effect parameter. The parameter is an integer and the value is an 601 * array of bytes. 602 * 603 * @see #getParameter(byte[], byte[]) 604 * @hide 605 */ 606 public int getParameter(int param, byte[] value) 607 throws IllegalStateException { 608 byte[] p = intToByteArray(param); 609 610 return getParameter(p, value); 611 } 612 613 /** 614 * Get effect parameter. The parameter is an integer and the value is an 615 * array of 1 or 2 integers 616 * 617 * @see #getParameter(byte[], byte[]) 618 * @hide 619 */ 620 public int getParameter(int param, int[] value) 621 throws IllegalStateException { 622 if (value.length > 2) { 623 return ERROR_BAD_VALUE; 624 } 625 byte[] p = intToByteArray(param); 626 627 byte[] v = new byte[value.length * 4]; 628 629 int status = getParameter(p, v); 630 631 value[0] = byteArrayToInt(v); 632 if (v.length > 4) { 633 value[1] = byteArrayToInt(v, 4); 634 } 635 return status; 636 } 637 638 /** 639 * Get effect parameter. The parameter is an integer and the value is an 640 * array of 1 or 2 short integers 641 * 642 * @see #getParameter(byte[], byte[]) 643 * @hide 644 */ 645 public int getParameter(int param, short[] value) 646 throws IllegalStateException { 647 if (value.length > 2) { 648 return ERROR_BAD_VALUE; 649 } 650 byte[] p = intToByteArray(param); 651 652 byte[] v = new byte[value.length * 2]; 653 654 int status = getParameter(p, v); 655 656 value[0] = byteArrayToShort(v); 657 if (v.length > 2) { 658 value[1] = byteArrayToShort(v, 2); 659 } 660 return status; 661 } 662 663 /** 664 * Get effect parameter. The parameter is an array of 1 or 2 integers and 665 * the value is also an array of 1 or 2 integers 666 * 667 * @see #getParameter(byte[], byte[]) 668 * @hide 669 */ 670 public int getParameter(int[] param, int[] value) 671 throws IllegalStateException { 672 if (param.length > 2 || value.length > 2) { 673 return ERROR_BAD_VALUE; 674 } 675 byte[] p = intToByteArray(param[0]); 676 if (param.length > 1) { 677 byte[] p2 = intToByteArray(param[1]); 678 p = concatArrays(p, p2); 679 } 680 byte[] v = new byte[value.length * 4]; 681 682 int status = getParameter(p, v); 683 684 value[0] = byteArrayToInt(v); 685 if (v.length > 4) { 686 value[1] = byteArrayToInt(v, 4); 687 } 688 return status; 689 } 690 691 /** 692 * Get effect parameter. The parameter is an array of 1 or 2 integers and 693 * the value is an array of 1 or 2 short integers 694 * 695 * @see #getParameter(byte[], byte[]) 696 * @hide 697 */ 698 public int getParameter(int[] param, short[] value) 699 throws IllegalStateException { 700 if (param.length > 2 || value.length > 2) { 701 return ERROR_BAD_VALUE; 702 } 703 byte[] p = intToByteArray(param[0]); 704 if (param.length > 1) { 705 byte[] p2 = intToByteArray(param[1]); 706 p = concatArrays(p, p2); 707 } 708 byte[] v = new byte[value.length * 2]; 709 710 int status = getParameter(p, v); 711 712 value[0] = byteArrayToShort(v); 713 if (v.length > 2) { 714 value[1] = byteArrayToShort(v, 2); 715 } 716 return status; 717 } 718 719 /** 720 * Get effect parameter. The parameter is an array of 1 or 2 integers and 721 * the value is an array of bytes 722 * 723 * @see #getParameter(byte[], byte[]) 724 * @hide 725 */ 726 public int getParameter(int[] param, byte[] value) 727 throws IllegalStateException { 728 if (param.length > 2) { 729 return ERROR_BAD_VALUE; 730 } 731 byte[] p = intToByteArray(param[0]); 732 if (param.length > 1) { 733 byte[] p2 = intToByteArray(param[1]); 734 p = concatArrays(p, p2); 735 } 736 737 return getParameter(p, value); 738 } 739 740 /** 741 * Send a command to the effect engine. This method is intended to send 742 * proprietary commands to a particular effect implementation. 743 * 744 * @hide 745 */ 746 public int command(int cmdCode, byte[] command, byte[] reply) 747 throws IllegalStateException { 748 checkState("command()"); 749 int[] replySize = new int[1]; 750 replySize[0] = reply.length; 751 752 int status = native_command(cmdCode, command.length, command, 753 replySize, reply); 754 755 if (reply.length > replySize[0]) { 756 byte[] resizedReply = new byte[replySize[0]]; 757 System.arraycopy(reply, 0, resizedReply, 0, replySize[0]); 758 reply = resizedReply; 759 } 760 return status; 761 } 762 763 // -------------------------------------------------------------------------- 764 // Getters 765 // -------------------- 766 767 /** 768 * Returns effect unique identifier. This system wide unique identifier can 769 * be used to attach this effect to a MediaPlayer or an AudioTrack when the 770 * effect is an auxiliary effect (Reverb) 771 * 772 * @return the effect identifier. 773 * @throws IllegalStateException 774 */ 775 public int getId() throws IllegalStateException { 776 checkState("getId()"); 777 return mId; 778 } 779 780 /** 781 * Returns effect enabled state 782 * 783 * @return true if the effect is enabled, false otherwise. 784 * @throws IllegalStateException 785 */ 786 public boolean getEnabled() throws IllegalStateException { 787 checkState("getEnabled()"); 788 return native_getEnabled(); 789 } 790 791 /** 792 * Checks if this AudioEffect object is controlling the effect engine. 793 * 794 * @return true if this instance has control of effect engine, false 795 * otherwise. 796 * @throws IllegalStateException 797 */ 798 public boolean hasControl() throws IllegalStateException { 799 checkState("hasControl()"); 800 return native_hasControl(); 801 } 802 803 // -------------------------------------------------------------------------- 804 // Initialization / configuration 805 // -------------------- 806 /** 807 * Sets the listener AudioEffect notifies when the effect engine is enabled 808 * or disabled. 809 * 810 * @param listener 811 */ 812 public void setEnableStatusListener(OnEnableStatusChangeListener listener) { 813 synchronized (mListenerLock) { 814 mEnableStatusChangeListener = listener; 815 } 816 if ((listener != null) && (mNativeEventHandler == null)) { 817 createNativeEventHandler(); 818 } 819 } 820 821 /** 822 * Sets the listener AudioEffect notifies when the effect engine control is 823 * taken or returned. 824 * 825 * @param listener 826 */ 827 public void setControlStatusListener(OnControlStatusChangeListener listener) { 828 synchronized (mListenerLock) { 829 mControlChangeStatusListener = listener; 830 } 831 if ((listener != null) && (mNativeEventHandler == null)) { 832 createNativeEventHandler(); 833 } 834 } 835 836 /** 837 * Sets the listener AudioEffect notifies when a parameter is changed. 838 * 839 * @param listener 840 * @hide 841 */ 842 public void setParameterListener(OnParameterChangeListener listener) { 843 synchronized (mListenerLock) { 844 mParameterChangeListener = listener; 845 } 846 if ((listener != null) && (mNativeEventHandler == null)) { 847 createNativeEventHandler(); 848 } 849 } 850 851 // Convenience method for the creation of the native event handler 852 // It is called only when a non-null event listener is set. 853 // precondition: 854 // mNativeEventHandler is null 855 private void createNativeEventHandler() { 856 Looper looper; 857 if ((looper = Looper.myLooper()) != null) { 858 mNativeEventHandler = new NativeEventHandler(this, looper); 859 } else if ((looper = Looper.getMainLooper()) != null) { 860 mNativeEventHandler = new NativeEventHandler(this, looper); 861 } else { 862 mNativeEventHandler = null; 863 } 864 } 865 866 // --------------------------------------------------------- 867 // Interface definitions 868 // -------------------- 869 /** 870 * The OnEnableStatusChangeListener interface defines a method called by the AudioEffect 871 * when a the enabled state of the effect engine was changed by the controlling application. 872 */ 873 public interface OnEnableStatusChangeListener { 874 /** 875 * Called on the listener to notify it that the effect engine has been 876 * enabled or disabled. 877 * @param effect the effect on which the interface is registered. 878 * @param enabled new effect state. 879 */ 880 void onEnableStatusChange(AudioEffect effect, boolean enabled); 881 } 882 883 /** 884 * The OnControlStatusChangeListener interface defines a method called by the AudioEffect 885 * when a the control of the effect engine is gained or lost by the application 886 */ 887 public interface OnControlStatusChangeListener { 888 /** 889 * Called on the listener to notify it that the effect engine control 890 * has been taken or returned. 891 * @param effect the effect on which the interface is registered. 892 * @param controlGranted true if the application has been granted control of the effect 893 * engine, false otherwise. 894 */ 895 void onControlStatusChange(AudioEffect effect, boolean controlGranted); 896 } 897 898 /** 899 * The OnParameterChangeListener interface defines a method called by the AudioEffect 900 * when a parameter is changed in the effect engine by the controlling application. 901 * @hide 902 */ 903 public interface OnParameterChangeListener { 904 /** 905 * Called on the listener to notify it that a parameter value has changed. 906 * @param effect the effect on which the interface is registered. 907 * @param status status of the set parameter operation. 908 * @param param ID of the modified parameter. 909 * @param value the new parameter value. 910 */ 911 void onParameterChange(AudioEffect effect, int status, byte[] param, 912 byte[] value); 913 } 914 915 916 // ------------------------------------------------------------------------- 917 // Audio Effect Control panel intents 918 // ------------------------------------------------------------------------- 919 920 /** 921 * Intent to launch an audio effect control panel UI. 922 * <p>The goal of this intent is to enable separate implementations of music/media player 923 * applications and audio effect control application or services. 924 * This will allow platform vendors to offer more advanced control options for standard effects 925 * or control for platform specific effects. 926 * <p>The intent carries a number of extras used by the player application to communicate 927 * necessary pieces of information to the control panel application. 928 * <p>The calling application must use the 929 * {@link android.app.Activity#startActivityForResult(Intent, int)} method to launch the 930 * control panel so that its package name is indicated and used by the control panel 931 * application to keep track of changes for this particular application. 932 * <p>The {@link #EXTRA_AUDIO_SESSION} extra will indicate an audio session to which the 933 * audio effects should be applied. If no audio session is specified, either one of the 934 * follownig will happen: 935 * <p>- If an audio session was previously opened by the calling application with 936 * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intent, the effect changes will 937 * be applied to that session. 938 * <p>- If no audio session is opened, the changes will be stored in the package specific 939 * storage area and applied whenever a new audio session is opened by this application. 940 * <p>The {@link #EXTRA_CONTENT_TYPE} extra will help the control panel application 941 * customize both the UI layout and the default audio effect settings if none are already 942 * stored for the calling application. 943 */ 944 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 945 public static final String ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL = 946 "android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL"; 947 948 /** 949 * Intent to signal to the effect control application or service that a new audio session 950 * is opened and requires audio effects to be applied. 951 * <p>This is different from {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} in that no 952 * UI should be displayed in this case. Music player applications can broadcast this intent 953 * before starting playback to make sure that any audio effect settings previously selected 954 * by the user are applied. 955 * <p>The effect control application receiving this intent will look for previously stored 956 * settings for the calling application, create all required audio effects and apply the 957 * effect settings to the specified audio session. 958 * <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the 959 * audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory. 960 * <p>If no stored settings are found for the calling application, default settings for the 961 * content type indicated by {@link #EXTRA_CONTENT_TYPE} will be applied. The default settings 962 * for a given content type are platform specific. 963 */ 964 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 965 public static final String ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION = 966 "android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION"; 967 968 /** 969 * Intent to signal to the effect control application or service that an audio session 970 * is closed and that effects should not be applied anymore. 971 * <p>The effect control application receiving this intent will delete all effects on 972 * this session and store current settings in package specific storage. 973 * <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the 974 * audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory. 975 * <p>It is good practice for applications to broadcast this intent when music playback stops 976 * and/or when exiting to free system resources consumed by audio effect engines. 977 */ 978 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 979 public static final String ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION = 980 "android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION"; 981 982 /** 983 * Contains the ID of the audio session the effects should be applied to. 984 * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL}, 985 * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and 986 * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents. 987 * <p>The extra value is of type int and is the audio session ID. 988 * @see android.media.MediaPlayer#getAudioSessionId() for details on audio sessions. 989 */ 990 public static final String EXTRA_AUDIO_SESSION = "android.media.extra.AUDIO_SESSION"; 991 992 /** 993 * Contains the package name of the calling application. 994 * <p>This extra is for use with {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and 995 * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents. 996 * <p>The extra value is a string containing the full package name. 997 */ 998 public static final String EXTRA_PACKAGE_NAME = "android.media.extra.PACKAGE_NAME"; 999 1000 /** 1001 * Indicates which type of content is played by the application. 1002 * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} and 1003 * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intents. 1004 * <p>This information is used by the effect control application to customize UI and select 1005 * appropriate default effect settings. The content type is one of the following: 1006 * <ul> 1007 * <li>{@link #CONTENT_TYPE_MUSIC}</li> 1008 * <li>{@link #CONTENT_TYPE_MOVIE}</li> 1009 * <li>{@link #CONTENT_TYPE_GAME}</li> 1010 * <li>{@link #CONTENT_TYPE_VOICE}</li> 1011 * </ul> 1012 * If omitted, the content type defaults to {@link #CONTENT_TYPE_MUSIC}. 1013 */ 1014 public static final String EXTRA_CONTENT_TYPE = "android.media.extra.CONTENT_TYPE"; 1015 1016 /** 1017 * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is music 1018 */ 1019 public static final int CONTENT_TYPE_MUSIC = 0; 1020 /** 1021 * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is video or movie 1022 */ 1023 public static final int CONTENT_TYPE_MOVIE = 1; 1024 /** 1025 * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is game audio 1026 */ 1027 public static final int CONTENT_TYPE_GAME = 2; 1028 /** 1029 * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is voice audio 1030 */ 1031 public static final int CONTENT_TYPE_VOICE = 3; 1032 1033 1034 // --------------------------------------------------------- 1035 // Inner classes 1036 // -------------------- 1037 /** 1038 * Helper class to handle the forwarding of native events to the appropriate 1039 * listeners 1040 */ 1041 private class NativeEventHandler extends Handler { 1042 private AudioEffect mAudioEffect; 1043 1044 public NativeEventHandler(AudioEffect ae, Looper looper) { 1045 super(looper); 1046 mAudioEffect = ae; 1047 } 1048 1049 @Override 1050 public void handleMessage(Message msg) { 1051 if (mAudioEffect == null) { 1052 return; 1053 } 1054 switch (msg.what) { 1055 case NATIVE_EVENT_ENABLED_STATUS: 1056 OnEnableStatusChangeListener enableStatusChangeListener = null; 1057 synchronized (mListenerLock) { 1058 enableStatusChangeListener = mAudioEffect.mEnableStatusChangeListener; 1059 } 1060 if (enableStatusChangeListener != null) { 1061 enableStatusChangeListener.onEnableStatusChange( 1062 mAudioEffect, (boolean) (msg.arg1 != 0)); 1063 } 1064 break; 1065 case NATIVE_EVENT_CONTROL_STATUS: 1066 OnControlStatusChangeListener controlStatusChangeListener = null; 1067 synchronized (mListenerLock) { 1068 controlStatusChangeListener = mAudioEffect.mControlChangeStatusListener; 1069 } 1070 if (controlStatusChangeListener != null) { 1071 controlStatusChangeListener.onControlStatusChange( 1072 mAudioEffect, (boolean) (msg.arg1 != 0)); 1073 } 1074 break; 1075 case NATIVE_EVENT_PARAMETER_CHANGED: 1076 OnParameterChangeListener parameterChangeListener = null; 1077 synchronized (mListenerLock) { 1078 parameterChangeListener = mAudioEffect.mParameterChangeListener; 1079 } 1080 if (parameterChangeListener != null) { 1081 // arg1 contains offset of parameter value from start of 1082 // byte array 1083 int vOffset = msg.arg1; 1084 byte[] p = (byte[]) msg.obj; 1085 // See effect_param_t in EffectApi.h for psize and vsize 1086 // fields offsets 1087 int status = byteArrayToInt(p, 0); 1088 int psize = byteArrayToInt(p, 4); 1089 int vsize = byteArrayToInt(p, 8); 1090 byte[] param = new byte[psize]; 1091 byte[] value = new byte[vsize]; 1092 System.arraycopy(p, 12, param, 0, psize); 1093 System.arraycopy(p, vOffset, value, 0, vsize); 1094 1095 parameterChangeListener.onParameterChange(mAudioEffect, 1096 status, param, value); 1097 } 1098 break; 1099 1100 default: 1101 Log.e(TAG, "handleMessage() Unknown event type: " + msg.what); 1102 break; 1103 } 1104 } 1105 } 1106 1107 // --------------------------------------------------------- 1108 // Java methods called from the native side 1109 // -------------------- 1110 @SuppressWarnings("unused") 1111 private static void postEventFromNative(Object effect_ref, int what, 1112 int arg1, int arg2, Object obj) { 1113 AudioEffect effect = (AudioEffect) ((WeakReference) effect_ref).get(); 1114 if (effect == null) { 1115 return; 1116 } 1117 if (effect.mNativeEventHandler != null) { 1118 Message m = effect.mNativeEventHandler.obtainMessage(what, arg1, 1119 arg2, obj); 1120 effect.mNativeEventHandler.sendMessage(m); 1121 } 1122 1123 } 1124 1125 // --------------------------------------------------------- 1126 // Native methods called from the Java side 1127 // -------------------- 1128 1129 private static native final void native_init(); 1130 1131 private native final int native_setup(Object audioeffect_this, String type, 1132 String uuid, int priority, int audioSession, int[] id, Object[] desc); 1133 1134 private native final void native_finalize(); 1135 1136 private native final void native_release(); 1137 1138 private native final int native_setEnabled(boolean enabled); 1139 1140 private native final boolean native_getEnabled(); 1141 1142 private native final boolean native_hasControl(); 1143 1144 private native final int native_setParameter(int psize, byte[] param, 1145 int vsize, byte[] value); 1146 1147 private native final int native_getParameter(int psize, byte[] param, 1148 int[] vsize, byte[] value); 1149 1150 private native final int native_command(int cmdCode, int cmdSize, 1151 byte[] cmdData, int[] repSize, byte[] repData); 1152 1153 private static native Object[] native_query_effects(); 1154 1155 // --------------------------------------------------------- 1156 // Utility methods 1157 // ------------------ 1158 1159 /** 1160 * @hide 1161 */ 1162 public void checkState(String methodName) throws IllegalStateException { 1163 synchronized (mStateLock) { 1164 if (mState != STATE_INITIALIZED) { 1165 throw (new IllegalStateException(methodName 1166 + " called on uninitialized AudioEffect.")); 1167 } 1168 } 1169 } 1170 1171 /** 1172 * @hide 1173 */ 1174 public void checkStatus(int status) { 1175 switch (status) { 1176 case AudioEffect.SUCCESS: 1177 break; 1178 case AudioEffect.ERROR_BAD_VALUE: 1179 throw (new IllegalArgumentException( 1180 "AudioEffect: bad parameter value")); 1181 case AudioEffect.ERROR_INVALID_OPERATION: 1182 throw (new UnsupportedOperationException( 1183 "AudioEffect: invalid parameter operation")); 1184 default: 1185 throw (new RuntimeException("AudioEffect: set/get parameter error")); 1186 } 1187 } 1188 1189 /** 1190 * @hide 1191 */ 1192 public int byteArrayToInt(byte[] valueBuf) { 1193 return byteArrayToInt(valueBuf, 0); 1194 1195 } 1196 1197 /** 1198 * @hide 1199 */ 1200 public int byteArrayToInt(byte[] valueBuf, int offset) { 1201 ByteBuffer converter = ByteBuffer.wrap(valueBuf); 1202 converter.order(ByteOrder.nativeOrder()); 1203 return converter.getInt(offset); 1204 1205 } 1206 1207 /** 1208 * @hide 1209 */ 1210 public byte[] intToByteArray(int value) { 1211 ByteBuffer converter = ByteBuffer.allocate(4); 1212 converter.order(ByteOrder.nativeOrder()); 1213 converter.putInt(value); 1214 return converter.array(); 1215 } 1216 1217 /** 1218 * @hide 1219 */ 1220 public short byteArrayToShort(byte[] valueBuf) { 1221 return byteArrayToShort(valueBuf, 0); 1222 } 1223 1224 /** 1225 * @hide 1226 */ 1227 public short byteArrayToShort(byte[] valueBuf, int offset) { 1228 ByteBuffer converter = ByteBuffer.wrap(valueBuf); 1229 converter.order(ByteOrder.nativeOrder()); 1230 return converter.getShort(offset); 1231 1232 } 1233 1234 /** 1235 * @hide 1236 */ 1237 public byte[] shortToByteArray(short value) { 1238 ByteBuffer converter = ByteBuffer.allocate(2); 1239 converter.order(ByteOrder.nativeOrder()); 1240 short sValue = (short) value; 1241 converter.putShort(sValue); 1242 return converter.array(); 1243 } 1244 1245 /** 1246 * @hide 1247 */ 1248 public byte[] concatArrays(byte[]... arrays) { 1249 int len = 0; 1250 for (byte[] a : arrays) { 1251 len += a.length; 1252 } 1253 byte[] b = new byte[len]; 1254 1255 int offs = 0; 1256 for (byte[] a : arrays) { 1257 System.arraycopy(a, 0, b, offs, a.length); 1258 offs += a.length; 1259 } 1260 return b; 1261 } 1262 } 1263